2011-04-28 96 views
10

假设你有一些像这样的代码:商店模板对象作为成员对象

struct Manager 
{ 
    template <class T> 
    void doSomething(T const& t) 
    { 
    Worker<T> worker; 
    worker.work(t); 
    } 
}; 

“管理员”对象被创建一次,并呼吁有几个diffent类型为“T”,但每种类型的T称为多倍。这可能是,以简化的形式,像

Manager manager; 
const int N = 1000; 
for (int i=0;i<N;i++) 
{ 
    manager.doSomething<int>(3); 
    manager.doSomething<char>('x'); 
    manager.doSomething<float>(3.14); 
} 

现在仿形揭示构建Worker<T>是一个耗时的操作,并且应当避免构建了N次(内doSomething<T>)。由于线程安全的原因,每个“经理”可以有一个Worker<int>,一个Worker<char>Worker<float>,但所有经理都没有一个Worker<int>。所以通常我会让“worker”成为一个变量。但是我怎么能在上面的代码中做到这一点? (我不知道哪个“T”会被使用)。

我发现了一个使用std :: map的解决方案,但它不完全类型安全,当然也不是很优雅。如果没有虚拟方法,你可以建议一种类型安全的方法,而不是每构建一个“T”更经常构建Worker<T>

(请注意Worker不是从任何模板参数的自由基类派生的)。

感谢您的任何解决方案!

+0

听起来像一个元组的工作,虽然我不熟悉语法,所以我不能提供一个例子。 – 2011-04-28 18:30:28

+1

如何让你的工人类每个类只有一个实例? (因为工作人员,工作人员,或工作人员,...或不同的类,它是可能的) - 工作人员 :: instance_get()。work(t) – 2011-04-28 18:55:38

+0

如果你很高兴使用'boost :: mpl',自己的问题和答案,你可能能够适应你的需求。 http://stackoverflow.com/questions/4798169/is-there-a-way-to-break-out-of-boostmpl-for-each – Nim 2011-04-28 19:27:28

回答

7

您可以使用像一个std::map<std::type_info,shared_ptr<void> >这样的:

#include <map> 
#include <typeinfo> 
#include <utility> 
#include <functional> 
#include <boost/shared_ptr.hpp> 

using namespace std; 
using namespace boost; 

// exposition only: 
template <typename T> 
struct Worker { 
    void work(const T &) {} 
}; 

// wrapper around type_info (could use reference_wrapper, 
// but the code would be similar) to make it usable as a map<> key: 
struct TypeInfo { 
    const type_info & ti; 
    /*implicit*/ TypeInfo(const type_info & ti) : ti(ti) {} 
}; 

// make it LessComparable (could spcialise std::less, too): 
bool operator<(const TypeInfo & lhs, const TypeInfo & rhs) { 
    return lhs.ti.before(rhs.ti); 
} 

struct Manager 
{ 
    map<TypeInfo,shared_ptr<void> > m_workers; 
    template <class T> 
    Worker<T> * findWorker() 
    { 
     const map<TypeInfo,shared_ptr<void> >::const_iterator 
     it = m_workers.find(typeid(T)); 
     if (it == m_workers.end()) { 
      const shared_ptr< Worker<T> > nworker(new Worker<T>); 
      m_workers[typeid(T)] = nworker; 
      return nworker.get(); 
     } else { 
      return static_cast<Worker<T>*>(it->second.get()); 
     } 
    } 
    template <typename T> 
    void doSomething(const T & t) { 
     findWorker<T>()->work(t); 
    } 
}; 

int main() { 

    Manager m; 
    m.doSomething(1); 
    m.doSomething(1.); 

    return 0; 
} 

这是类型安全的,因为我们使用type_info作为地图的索引。另外,即使工作人员处于shared_ptr<void> s中,工作人员也会被正确删除,因为删除人员是从原始shared_ptr<Worker<T> >开始复制的,并且该人员会调用正确的构造函数。它也不使用虚拟函数,尽管所有类型的擦除(这是一种)使用虚拟函数的地方。这里是shared_ptr

保理从findWorker模板无关的代码放到一个非模板功能,减少代码膨胀是

由于作为练习留给读者:)给谁指出使用type_info作为的错误都提意见直接键。

+0

这似乎确实符合所有OP的要求。 – 2011-04-28 18:53:41

+3

@mmutz:type_info不可分配。你尝试编译这个吗? – 2011-04-28 19:13:27

+1

@mmutz:'type_info'也不需要提供'operator <'。 – 2011-04-28 19:28:36

2

您可以添加boost::variant s或boost::any s的std::vector作为您班级的成员。并追加任何你想要的工作。

编辑:代码波纹管会解释

struct Manager 
{ 
    std::vector<std::pair<std::type_info, boost::any> > workers; 
    template <class T> 
    void doSomething(T const& t) 
    { 
    int i = 0; 
    for(; i < workers.size(); ++i) 
     if(workers[i].first == typeid(T)) 
      break; 
    if(i == workers.size()) 
     workers.push_back(std::pair<std::type_info, boost::any>(typeid(T).name(), Worker<T>()); 
    any_cast<T>(workers[i]).work(t); 
    } 
}; 
+0

这比其他解决方案更优雅。这个矢量首先让我感到害怕,但它在实践中有多大? – 2011-04-29 16:12:28

+0

我也认为不同工作者的数量很小,因此可以将它们存储在向量中(或者列表中)。 – 2011-04-29 16:15:00

0

这样的事情是如何将工作:

struct Base { }; 
template<class T> struct D : public Base { Manager<T> *ptr; }; 
... 
struct Manager { 
    ... 
Base *ptr; 
}; 
2

我已经在研究类似于mmutz的回答,他发布了他的答案。这是一个在GCC 4.4.3下编译和运行的完整解决方案。它使用RTTI和多态性来构造Worker<T>并将它们存储在地图中。

#include <iostream> 
#include <typeinfo> 
#include <map> 

struct BaseWorker 
{ 
    virtual ~BaseWorker() {} 
    virtual void work(const void* x) = 0; 
}; 

template <class T> 
struct Worker : public BaseWorker 
{ 
    Worker() 
    { 
     /* Heavyweight constructor*/ 
     std::cout << typeid(T).name() << " constructor\n"; 
    } 

    void work(const void* x) {doWork(*static_cast<const T*>(x));} 

    void doWork(const T& x) 
     {std::cout << typeid(T).name() << "::doWork(" << x << ")\n";} 
}; 

struct TypeofLessThan 
{ 
    bool operator()(const std::type_info* lhs, const std::type_info* rhs) const 
     {return lhs->before(*rhs);} 
}; 

struct Manager 
{ 
    typedef std::map<const std::type_info*, BaseWorker*, TypeofLessThan> WorkerMap; 

    ~Manager() 
    { 
     // Delete all BaseWorkers in workerMap_ 
     WorkerMap::iterator it; 
     for (it = workerMap_.begin(); it != workerMap_.end(); ++it) 
      delete it->second; 
    } 

    template <class T> 
    void doSomething(T const& x) 
    { 
     WorkerMap::iterator it = workerMap_.find(&typeid(T)); 
     if (it == workerMap_.end()) 
     { 
      it = workerMap_.insert(
       std::make_pair(&typeid(T), new Worker<T>)).first; 
     } 
     Worker<T>* worker = static_cast<Worker<T>*>(it->second); 
     worker->work(&x); 
    } 

    WorkerMap workerMap_; 
}; 

int main() 
{ 
    Manager manager; 
    const int N = 10; 
    for (int i=0;i<N;i++) 
    { 
     manager.doSomething<int>(3); 
     manager.doSomething<char>('x'); 
     manager.doSomething<float>(3.14); 
    } 
} 

map<std::type_info, BaseWorker*>不起作用,因为type_info没有拷贝构造。我曾经使用map<const std::type_info*, BaseWorker*>。我只需要检查typeid(T)是否总是返回相同的引用(我认为是这样)。


不要紧typeid(T)是否返回相同的参考,因为我总是用type_info::before做所有的比较。

+0

采用'type_info'的地址是否安全? http://msdn.microsoft.com/en-us/library/70ky2y6k(v=vs.80).aspx说“构建(临时)对象”,我真的不知道如何阅读该语句。 – pmr 2011-04-28 19:48:42

+0

@Emile:你不会在'doSomething()'中将't'传递给'work()'。 – 2011-04-28 20:10:39

+0

@mmutz:固定。谢谢。 – 2011-04-28 20:17:50