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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 21:05:38  来源:igfitidea点击:

Writing variadic template constructor

c++c++11variadic-templates

提问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 newXobject 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::vectoritself 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)...};
}

Live version

现场版

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

 }