在可变参数模板的上下文中,“…”令牌的规则是什么?


98

在C ++ 11中,有这样的可变参数模板:

template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args )
{
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

对此有一些好奇:表达式同时std::forward<Args>(args)...使用Argsargs仅使用一个...标记。此外,还有std::forward一种仅采用一个模板参数和一个自变量的非可变模板函数。(大致)有什么语法规则?如何将其概括?

另外:在函数实现中,省略号(...)位于关注表达式的末尾。是否有原因在模板参数列表和参数列表中省略号位于中间?


2
在第二部分中简要介绍:当“声明”模板参数包或功能参数包时,...出现在标识符之前。当使用其中一种或两种类型的背包时,...表达式模式之后会出现来扩展。
aschepler

Answers:


99

在可变参数模板的上下文中,...如果模板参数包出现在表达式的右侧(使用此表达式模式片刻),则省略号用于解压缩模板参数包。规则是,重复左侧的任何模式... -拆开的模式(现在称为表达式)用逗号分隔,

通过一些示例可以最好地理解它。假设您具有以下功能模板:

template<typename ...T>
void f(T ... args) 
{
   g( args... );        //pattern = args
   h( x(args)... );     //pattern = x(args)
   m( y(args...) );     //pattern = args (as argument to y())
   n( z<T>(args)... );  //pattern = z<T>(args)
}

现在,如果我调用该函数传递T{int, char, short},则每个函数调用的扩展为:

g( arg0, arg1, arg2 );           
h( x(arg0), x(arg1), x(arg2) );
m( y(arg0, arg1, arg2) );
n( z<int>(arg0), z<char>(arg1), z<short>(arg2) );

在您发布的代码中,std::forward遵循n()函数调用说明的第四个模式。

需要注意的区别x(args)...y(args...)上面!


您还可以使用以下...方式初始化数组:

struct data_info
{
     boost::any  data;
     std::size_t type_size;
};

std::vector<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}

扩展为:

std::vector<data_info> v 
{ 
   {arg0, sizeof(int)},
   {arg1, sizeof(char)},
   {arg2, sizeof(short)}
};

我刚刚意识到一个模式甚至可以包含访问说明符,例如public,如以下示例所示:

template<typename ... Mixins>
struct mixture : public Mixins ...  //pattern = public Mixins
{
    //code
};

在此示例中,模式扩展为:

struct mixture__instantiated : public Mixin0, public Mixin1, .. public MixinN  

也就是说,mixture派生公开所有的基类。

希望能有所帮助。


1
关于匹配的表达式;它应该是最大可能的表达方式,不是吗?例如x+args...应扩展为x+arg0,x+arg1,x+arg2,而不是x+arg0,arg1,arg2
bitmask

因此,...适用于模式中的每个可扩展实体。
Lightness Races in Orbit

@bitmask:是的。x+args...应该扩展到x+arg0,x+arg1,x+arg2而不是 x+arg0,arg1,arg2
纳瓦兹

1
@ Jarod42:C ++ 17发布后,我将更新此答案。
纳瓦兹2015年

3
@synther:sizeof...(T)在那里不需要。您可以简单地写:int a[] = { ___ };
Nawaz

48

以下内容摘自Andrei Alexandrescu在GoingNative 2012上的演讲“可变参数模板是Funadic”。我可以推荐它作为可变参数模板的良好介绍。


一个可变参数包可以做两件事。可以申请sizeof...(vs)获取元素数量并进行扩展。

扩充规则

Use            Expansion

Ts...          T1, ..., Tn
Ts&&...        T1&&, ..., Tn&&
x<Ts,Y>::z...  x<T1,Y>::z, ..., x<Tn,Y>::z
x<Ts&,Us>...   x<T1&,U1>, ..., x<Tn&,Un>
func(5,vs)...  func(5,v1), ..., func(5,vn)

扩展向内向外扩展。锁定步骤扩展两个列表时,它们的大小必须相同。

更多示例:

gun(A<Ts...>::hun(vs)...);

展开Ts的模板参数列表中的all ,A然后hun使用all扩展功能vs

gun(A<Ts...>::hun(vs...));

展开Ts的模板参数列表中的A全部和全部vs作为的函数参数hun

gun(A<Ts>::hun(vs)...);

hun通过Tsvs逐步扩展功能。

注意:

Ts不是类型,vs也不是价值!它们是类型/值列表的别名。这两个列表都可能为空。两者都服从特定的动作。因此,以下操作是不可能的:

typedef Ts MyList;  // error!
Ts var;             // error!
auto copy = vs;     // error!

扩展基因座

函数参数

template <typename... Ts>
void fun(Ts... vs)

初始化列表

any a[] = { vs... };

基本说明符

template <typename... Ts>
struct C : Ts... {};
template <typename... Ts>
struct D : Box<Ts>... { /**/ };

成员初始化器列表

// Inside struct D
template <typename... Us>
D(Us... vs) : Box<Ts>(vs)... {}

临时参数列表

std::map<Ts...> m;

仅在参数可能匹配时才编译。

捕获列表

template <class... Ts> void fun(Ts... vs) {
    auto g = [&vs...] { return gun(vs...); }
    g();
}

属性列表

struct [[ Ts... ]] IAmFromTheFuture {};

它在规范中,但是还没有可以表示为类型的属性。


真好 函数自变量被排除在基因座之外,尽管:P
Potatoswatter

@Potatoswatter谢谢。没有提到函数参数列表中的扩展。
typ1232 2013年

@ typ1232抱歉,无法编辑,但我认为您的原始帖子太过窃。顺便说一句,我前段时间也看过那个话题-太棒了。
沃尔特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.