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;
}

A和的签名B是固定的(例如不能更改为 int[])。上下文:我正在考虑一个包装器,它将(专用)容器类型作为模板参数,例如T=vector<int>,或T=map<int,int>,当需要调用构造函数时会出现问题。

4

2 回答 2

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);

(真正的程序员当然更喜欢 member std::unique_ptr<T> _t;,否则语义不变,但允许您忽略所有注释。)

于 2012-04-06T19:40:31.477 回答
0

我相信 Kerrek SB 的回答部分正确,但不完整。它失败了,因为它C<T>的构造函数过于通用。也就是说,C<T>如果你只看它的构造函数声明,它将从任何东西构造。在您选择构造函数并实例化之前,您不会发现其他情况。到那时已经太晚了。

具体例子:

假设C<T>有:

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

现在你想C<T>在 a 中制作密钥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)...))
    {
    }
    // ...
};

即向构造函数添加一个约束,这样如果它不工作就不会被实例化。这是乱七八糟的,丑陋的,无论如何。也许你想用一个宏来装扮它。美好的。但是它使这个类在我上面提到的例子中被破坏的地方工作(以及许多其他问题,这些问题往往会在几年的时间里一次一个地报告错误)。

除了 Kerrek SB 关于在unique_ptr<T>此处使用原始指针的好建议之外,我还想补充一点:

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

  2. 考虑将 aT而不是(可能是智能的)指针存储到T. 除非您实际上试图指向基类以实现运行时多态性,否则您不需要指针语义。

总而言之:警惕过度泛型的代码,以及过度泛型的构造函数的彻头彻尾的偏执狂。

于 2012-05-28T01:46:04.103 回答