编写可变参数模板构造函数

时间:2015-03-04 22:43:06

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

最近我问this问题,但现在我想扩展它。我写了以下课程:

template <class T>
class X{
public:
    vector<T> v;
    template <class T>
    X(T n) {
        v.push_back(n);
    }
    template <class T, class... T2>
    X(T n, T2... rest) {
        v.push_back(n);
        X(rest...);
    }
};

使用

创建对象时
X<int> obj(1, 2, 3);  // obj.v containts only 1

Vector仅包含第一个值,但不包含其他值。我已经检查并看到构造函数被调用了3次,所以我可能正在创建临时对象并用其余参数填充它们的向量。我该如何解决这个问题?

4 个答案:

答案 0 :(得分:30)

首先,你的代码不能为我编译。

main.cpp:7:15: error: declaration of ‘class T’
     template <class T>
               ^
main.cpp:3:11: error:  shadows template parm ‘class T’
 template <class T>
           ^

我将外部的一个更改为U

template <class U>
class X{
public:
    vector<U> v;
    template <class T>
    X(T n) {
        v.push_back(n);
    }
    template <class T, class... T2>
    X(T n, T2... rest) {
        v.push_back(n);
        X(rest...);
    }
};

这是正确的,这会导致您在问题详细信息中提出的问题......

X<int> obj(1, 2, 3);  // obj.v containts only 1

这是因为构造函数末尾的语句X(rest...)不会递归调用构造函数继续初始化同一个对象;它会创建一个 new X对象然后抛弃它。一旦构造函数的 body 开始执行,就不再可能在同一个对象上调用另一个构造函数。委派必须在 ctor-initializer 中进行。例如,您可以这样做:

template <class T, class... T2>
X(T n, T2... rest): X(rest...) {
    v.insert(v.begin(), n);
}

虽然很糟糕,因为在向量的开头插入效率不高。

最好采用std::initializer_list<T>参数。这就是std::vector本身所做的事情。

X(std::initializer_list<U> il): v(il) {}
// ...
X<int> obj {1, 2, 3};

答案 1 :(得分:5)

完全同意Brian的回答,但即使正确的方法是另一种方法(即使用initializer_list),请注意(为了在其他情况下正确执行此操作),在这种情况下可变模板递归可能是通过注意空包是一个有效的模板参数包更加简单,以便最后一次使用空包调用可变参数模板构造函数,这将导致尝试调用默认的ctor,这可以是默认的终止递归。

以下,以下内容可行,IMO会更清晰:

template <class T>
struct X
{
    std::vector<T> v;
    X() = default; //Terminating recursion

    template <class U, class... Ts>
    X(U n, Ts... rest)  : X(rest...) { .. the recursive work ..}
};

同样,我并不是说模板和递归在这里是正确的,我只是指出它们是必要的,有一种更简单的方法可以使用它们。

答案 2 :(得分:3)

首先不需要递归 -
你可以使用“临时数组”成语并写

template <class... E>
X(E&&... e) {
    int temp[] = {(v.push_back(e), 0)...};
}

这种方法的正确(和更复杂)版本如下所示:

template <class... E>
X(E&&... e) {
    (void)std::initializer_list<int>{(v.push_back(std::forward<E>(e)), void(), 0)...};
}

Live version

注意,下一版本的C ++可能会有Fold Expressions
(v.push_back(e), ...);

答案 3 :(得分:0)

template <class T, class... Rest>
  X(T n, Rest... rest) {
   add(rest...);
 }

//Just another member function
template<class T>
  void add(T n) {
    v.push_back(n);
 }
  template<class T, class ... Rest>
   void add(T n, Rest... rest) {
     v.push_back(n);
     add(rest...);

 }