2012-04-06 90 views
0

如何編寫一個模板,該模板將作爲參數類,其構造函數具有互斥的簽名?具有各種構造函數簽名的類的模板

class A 
{ 
    A(){}; 
public: 
    int a; 
    A(int i) : a(i) {}; 
}; 

class B{ 
    B(){}; 
public: 
    int a,b; 
    B(int i,int j) : a(i), b(j) {}; 
}; 


template <class T> class C { 
public: 
    T* _t; 
    C(int i[]) 
    {      //??? 
    _t=new T(i[0]);  //if T has T(int) signature 
    _t=new T(i[0],i[1]); //if T has T(int,int) signature 
    } 
    ~C() {delete _t;} 
}; 

int main() 
{ 
    int Ai[]={1,2}; 
    C<A> _c(Ai); // template should work instantiated with A and B 
    C<B> _c(Ai); // 
    return 0; 
} 

AB的簽名是固定的(不能被更改爲int [例如])。上下文:我在考慮一個包裝器,它將採用(專用)容器類型作爲模板參數,例如T=vector<int>T=map<int,int>,當需要調用構造函數時會出現問題。

回答

4

使用variadically爲模板的構造函數:

template <typename T> struct C 
{ 
    template <typename ...Args> C(Args &&... args) 
    : _t(new T(std::forward<Args>(args)...)) 
    { 
    } 

    // ... destructor? Rule of five? Don't use pointers!!! 
private: 
    T * _t; // ouch! 
}; 

用法:

C<A> x(1); 
C<B> y(2, 3); 

(當然,真正的程序員寧願成員std::unique_ptr<T> _t;,與其餘部分不變語義,但是允許你不顧一切的)

+0

謝謝。美麗的東西。 – 2012-04-06 20:26:06

0

我相信Kerrek SB的回答是部分正確的,但並不完整。它的失敗在於C<T>的構造函數過於通用。也就是說,C<T>將從任何東西構造,如果你只是看它的構造函數聲明。除非你選擇了構造函數並且你正在實例化,否則你不會發現。到那時已經太晚了。

具體的例子:

假設C<T>有:

friend bool operator<(const C&, const C&); 

現在你想C<T>關鍵在map

std::map<C<A>, int> m; 
// ... 
m.erase(m.begin()); 

這是一個錯誤,因爲有是兩個erase過載現在看起來像:

iterator erase(const_iterator position); 
size_type erase(const key_type& k); 

and m.begin()iterator。這iterator將輕鬆轉換爲const_iteratorkey_type(又名C<A>)。

現在,這可以固定致電:

m.erase(m.cbegin()); 

代替。但這只是過度通用構造函數導致問題的冰山一角。例如任何代碼分支上:

std::is_constructible<C<A>, any type and any number of them>::value 

可能得到誤報,因爲上面會總是返回true。

解決方法是有點亂,但很實用:

template<typename T> 
struct C 
{ 
    template <class ...Args, 
       class = typename std::enable_if 
         < 
         std::is_constructible<T, Args...>::value 
         >::type 
      > 
    C(Args&& ...args) 
     : _t(new T(std::forward<Args>(args)...)) 
    { 
    } 
    // ... 
}; 

即向構造函數添加一個約束,使得它不會被實例化,如果它不起作用。這是混亂,醜陋的,無論如何。也許你想打扮一個宏。精細。但它使得這個類工作其中否則它是破碎在我上面提到的例子(和許多其他問題,傾向於隨着幾年的時間一次錯誤報告滴入)。

除了Kerrek SB的使用unique_ptr<T>在原始指針在這裏很好的意見,我想補充:

  1. ,此構造也許應該explicit,至少直到它被實際使用所示它確實需要隱含的案例。

  2. 考慮存儲T而不是指向T的指針(可能是智能指針)。除非實際上試圖指向基類來實現運行時多態,否則不需要指針語義。

總結:謹慎使用過於通用的代碼,以及過度泛型構造函數的徹頭徹尾的偏執。

相關問題