2012-04-06 91 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的指针(可能是智能指针)。除非实际上试图指向基类来实现运行时多态,否则不需要指针语义。

总结:谨慎使用过于通用的代码,以及过度泛型构造函数的彻头彻尾的偏执。

相关问题