具有各种构造函数签名的类的模板

时间:2012-04-06 19:34:49

标签: c++ templates c++11 variadic-templates

如何编写一个模板,它将作为参数类,其构造函数具有互斥的签名?

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>,当需要调用构造函数时会出现问题。

2 个答案:

答案 0 :(得分:4)

使用可变模板化的构造函数:

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;,其他语义不变,但允许你忽略所有的评论。)

答案 1 :(得分: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);

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)...))
    {
    }
    // ...
};

即。向构造函数添加一个约束,以便在它不起作用时不会实例化它。这是混乱,丑陋,无论如何。也许你想用宏来装扮它。精细。但它使这个类工作,其中否则在我上面提到的示例中已经 (以及其他许多其他问题都会像bug一样涓涓细流在一段时间内一次报告一次。)

除了Kerrek SB在这里使用unique_ptr<T>优于原始指针的好建议之外,我想补充一下:

  1. 这个构造函数可能应该是explicit,至少在实际用例中显示它确实需要隐式。

  2. 考虑将T而不是(可能智能)指针存储到T。除非你真的试图指向一个基类来实现运行时多态,否则你不需要指针语义。

  3. 总结:警惕过于通用的代码,以及对过于通用的构造函数的彻头彻尾的偏执。