理解可变参数模板函数中的点

Raj*_*war 5 c++ templates variadic-templates c++11

假设我有以下代码.我在很大程度上理解这一点.

template <class ...Args>               //---> Statement A
void foo_imp(const Args&... args)      //---> Statement B
{
    std::vector<int> vec = {args...};  //---> Statement C
}
Run Code Online (Sandbox Code Playgroud)

现在我对...应该在变量名称之前或之后出现的位置感到困惑.这就是为什么我感到困惑.

声明A,如下所示

template <class ...Args>  
Run Code Online (Sandbox Code Playgroud)

表明这...是变量类型,Args是变量名称(很好)

声明B,如下

void foo_imp(const Args&... args) 
Run Code Online (Sandbox Code Playgroud)

在我看来这种类型是Args,它是模板类型,但是...后面的添加Args让我困惑?

然后将值赋给向量进一步令人困惑

 std::vector<int> vec = { args... };
Run Code Online (Sandbox Code Playgroud)

...args之后为什么会这样?

关于我如何记住上述代码的任何想法,以便它对我有意义?

Man*_*rse 17

<class ...Args>声明Args是一个可变参数包,其中每个参数的(meta-)类型是class.为了便于解释,我们假设这可以被认为类似于:

<class Args1, class Args2, class Args3, etc.>
Run Code Online (Sandbox Code Playgroud)

变量类型参数包可以出现在可能出现类型列表的位置,但必须使用扩展....所以例如void foo_imp(Args)不合法,但是

void foo_imp(Args...)
Run Code Online (Sandbox Code Playgroud)

相当于

void foo_imp(Args1, Args2, Args3, etc.) 
Run Code Online (Sandbox Code Playgroud)

...东西扩展到其左侧,使用参数组的值.所以在你的实际例子中,

void foo_imp(const Args&...)    
Run Code Online (Sandbox Code Playgroud)

相当于

void foo_imp(const Args1&, const Args2&, const Args3&, etc.)  
Run Code Online (Sandbox Code Playgroud)

现在是时候放弃模板参数包的想法作为固定大小的类型列表; 实际上,它的长度由呼叫时提供的参数数量决定.

"但是我们不打算在电话时间提供任何模板参数!?!" 我听你说.这是因为它foo_imp是一个函数,其模板参数是根据其正常参数推导出来的.这意味着(const Args1&, const Args2&, const Args3&)列表的长度由函数调用确定,并且每种ArgsN类型由函数模板类型推导的常规规则确定.


现在开始args.与之类似Args,args是一个可变参数包,因此必须在使用时进行扩展.

std::vector<int> vec = {args...};
Run Code Online (Sandbox Code Playgroud)

相当于(使用我们之前的解释性命名系统)

std::vector<int> vec = {args1, args2, args3, etc.};
Run Code Online (Sandbox Code Playgroud)

变量参数包不会自动扩展,因为它们可以作为较大表达式的一部分进行扩展,因此自动扩展可能不明确或不正确.例如:

std::forward<Args>(args)...
//expands as:
std::forward<Args1>(args1), std::forward<Args2>(args2), std::forward<Args3>(args3), etc.
Run Code Online (Sandbox Code Playgroud)

能够工作,因为它单独转发每个参数.如果它被扩展为

std::forward<Args...>(args...)
//expands as:
std::forward<Args1, Args2, Args3, etc.>(args1, args2, args3, etc)    
Run Code Online (Sandbox Code Playgroud)

它甚至不会编译,因为std::forward只需要一个参数.


结论.如果...出现在包含任何可变参数包的表达式的右侧,则它将该表达式扩展为列表,列表中的每个元素都具有来自每个包含的参数包的第n个值而不是该参数包.

//Foo and bar are variadic parameter packs
Foo...                     => Foo1, Foo2, Foo3, etc
std::vector<Foo>...        => std::vector<Foo1>, std::vector<Foo2>, std::vector<Foo3>, etc.
std::tuple<Foo...>(bar...) => std::tuple<Foo1, Foo2, Foo3, etc>(bar1, bar2, bar3, etc)
&bar...                    => &bar1, &bar2, &bar3, etc
Run Code Online (Sandbox Code Playgroud)

如果这样的扩展表达式出现在参数列表中,则可以通过可变参数包变量捕获它.在这种情况下,如果表达式不包含任何可变参数包,则释放参数列表以获取任意数量的参数.

template<typename ...Baz> => Baz is a variable length variadic parameter pack of `typename`s
void bar(Baz ...baz)      => baz is a parameter pack of `Baz`s
void bar(Baz &&...baz)    => baz is a parameter pack of `Baz&&`s
Run Code Online (Sandbox Code Playgroud)