2011-04-28 99 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