C++ 编写可变参数模板构造函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28866559/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Writing variadic template constructor
提问by Tracer
Recently I asked thisquestion but now I would like to expand it. I wrote the following 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...);
}
};
When creating an object using
使用创建对象时
X<int> obj(1, 2, 3); // obj.v containts only 1
Vector only contains the first value, but not others. I've checked and saw that constructor is called 3 times, so I'm probably creating temp objects and filling their vectors with the rest of the arguments. How do I solve this problem?
Vector 只包含第一个值,但不包含其他值。我已经检查并看到构造函数被调用了 3 次,所以我可能正在创建临时对象并用其余的参数填充它们的向量。我该如何解决这个问题?
回答by Brian
First, your code doesn't compile for me.
首先,你的代码不适合我编译。
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>
^
I changed the outer one to U
.
我把外面的改成了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...);
}
};
You're correct that this causes the issue you gave in the question details...
您是正确的,这会导致您在问题详细信息中给出的问题...
X<int> obj(1, 2, 3); // obj.v containts only 1
This is because the statement X(rest...)
at the end of your constructor doesn't recursively call the constructor to continue initializing the same object; it creates a newX
object and then throws it away. Once a constructor's bodybegins to execute, it's no longer possible to invoke another constructor on the same object. Delegation must occur in the ctor-initializer. So for example, you could do this:
这是因为X(rest...)
构造函数末尾的语句不会递归调用构造函数来继续初始化同一个对象;它创建一个新X
对象,然后将其丢弃。一旦构造函数的主体开始执行,就不能再在同一对象上调用另一个构造函数。委托必须发生在ctor-initializer 中。例如,你可以这样做:
template <class T, class... T2>
X(T n, T2... rest): X(rest...) {
v.insert(v.begin(), n);
}
That sucks though, because inserting at the beginning of a vector isn't efficient.
但这很糟糕,因为在向量的开头插入效率不高。
Better to take a std::initializer_list<T>
argument. This is what std::vector
itself does.
最好进行std::initializer_list<T>
辩论。这就是std::vector
它本身所做的。
X(std::initializer_list<U> il): v(il) {}
// ...
X<int> obj {1, 2, 3};
回答by abigagli
Totally agree with Brian's answer, but even though the right approach is another (i.e. use initializer_list) please be aware (for the sake of doing this correctly in other circumstances) that variadic template recursion in this case could be much simpler just by noting that an empty pack is a valid template parameter pack, so that the variadic template constructor will be called one final time with the empty pack, which will lead to trying to call a default ctor, which just can be defaulted and terminate the recursion.
完全同意 Brian 的回答,但即使正确的方法是另一种(即使用 initializer_list),请注意(为了在其他情况下正确执行此操作),在这种情况下,可变参数模板递归可能会简单得多,只需注意空包是一个有效的模板参数包,因此可变参数模板构造函数将在最后一次与空包一起调用,这将导致尝试调用默认构造函数,该构造函数可以被默认并终止递归。
IOW, the following would work and would be much cleaner IMO:
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 ..}
};
Again, I'm not saying templates and recursion are the right thing here, I'm just pointing out that would they be necessary, there would be a simpler way to use them.
同样,我并不是说模板和递归在这里是正确的,我只是指出它们是否必要,会有更简单的使用方法。
回答by Abyx
There is no need for recursion in the first place -
you can use the "temporary array" idiom and write
首先不需要递归 -
您可以使用“临时数组”习语并编写
template <class... E>
X(E&&... e) {
int temp[] = {(v.push_back(e), 0)...};
}
The proper (and more complicated) version of this approach looks like this:
这种方法的正确(更复杂)版本如下所示:
template <class... E>
X(E&&... e) {
(void)std::initializer_list<int>{(v.push_back(std::forward<E>(e)), void(), 0)...};
}
Note, than the next version of C++ will probably have the Fold Expressions(v.push_back(e), ...);
注意,下一个版本的 C++ 可能会有折叠表达式(v.push_back(e), ...);
回答by fengzi
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...);
}