编写可变模板构造函数
Writing variadic template constructor
最近我问了 个问题,但现在我想扩展它。我写了以下 class:
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 次,所以我可能正在创建临时对象并用其余参数填充它们的向量。我该如何解决这个问题?
首先,你的代码不适合我编译。
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};
一开始就不需要递归-
你可以使用 "temporary array" 成语并写成
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)...};
}
注意,下一个版本的 C++ 可能会有 Fold Expressions
(v.push_back(e), ...);
完全同意 Brian 的回答,但即使正确的方法是另一种(即使用 initializer_list),请注意(为了在其他情况下正确执行此操作)在这种情况下可变参数模板递归只需注意空包是一个有效的模板参数包就可以简单得多,这样可变参数模板构造函数将最后一次用空包调用,这将导致尝试调用默认构造函数,这可以是默认并终止递归。
IOW,下面的方法可以工作并且在 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 ..}
};
再说一次,我并不是说模板和递归在这里是正确的,我只是指出如果它们是必需的,会有更简单的使用方法。
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...);
}
最近我问了
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 次,所以我可能正在创建临时对象并用其余参数填充它们的向量。我该如何解决这个问题?
首先,你的代码不适合我编译。
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};
一开始就不需要递归-
你可以使用 "temporary array" 成语并写成
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)...};
}
注意,下一个版本的 C++ 可能会有 Fold Expressions
(v.push_back(e), ...);
完全同意 Brian 的回答,但即使正确的方法是另一种(即使用 initializer_list),请注意(为了在其他情况下正确执行此操作)在这种情况下可变参数模板递归只需注意空包是一个有效的模板参数包就可以简单得多,这样可变参数模板构造函数将最后一次用空包调用,这将导致尝试调用默认构造函数,这可以是默认并终止递归。
IOW,下面的方法可以工作并且在 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 ..}
};
再说一次,我并不是说模板和递归在这里是正确的,我只是指出如果它们是必需的,会有更简单的使用方法。
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...);
}