A pattern标准中定义如下[温度变量]/4 http://eel.is/c++draft/temp.variadic#4: (via @t.c. https://stackoverflow.com/users/2756719/t-c)
A 包扩展由一个pattern和一个省略号,其实例化会在列表中产生零个或多个模式实例化(如下所述)。模式的形式取决于扩展发生的上下文。包扩展可能发生在以下环境中:
- 在函数参数包中 ([dcl.fct http://eel.is/c++draft/dcl.fct]);模式是参数声明 http://eel.is/c++draft/dcl.fct#parameter-declaration没有省略号。
- In a template parameter pack that is a pack expansion ([temp.param http://eel.is/c++draft/temp.param]):
- 如果模板参数包是参数声明 http://eel.is/c++draft/dcl.fct#parameter-declaration;模式是参数声明 http://eel.is/c++draft/dcl.fct#parameter-declaration没有省略号;
- 如果模板参数包是类型参数 http://eel.is/c++draft/temp.param#type-parameter with a 模板参数列表 http://eel.is/c++draft/temp#template-parameter-list;图案是对应的类型参数 http://eel.is/c++draft/temp.param#type-parameter没有省略号。
- In an 初始化列表 http://eel.is/c++draft/dcl.init#initializer-list ([dcl.init http://eel.is/c++draft/dcl.fct]);该模式是一个初始化子句 http://eel.is/c++draft/dcl.init#initializer-clause.
- In a 基本说明符列表 http://eel.is/c++draft/class.derived#base-specifier-list(子句[派生类 http://eel.is/c++draft/class.derived];该模式是碱基说明符 http://eel.is/c++draft/class.derived#base-specifier.
- In a 内存初始化列表 http://eel.is/c++draft/class.base.init#mem-initializer-list ([类.base.init http://eel.is/c++draft/class.base.init]) 为一个内存初始化器 http://eel.is/c++draft/class.base.init#mem-initializer whose 内存初始化器 ID http://eel.is/c++draft/class.base.init#mem-initializer-id表示基类;模式是内存初始化器 http://eel.is/c++draft/class.base.init#mem-initializer.
- In a 模板参数列表 http://eel.is/c++draft/temp.names#template-argument-list ([temp.arg http://eel.is/c++draft/temp.arg]);该模式是模板参数 http://eel.is/c++draft/temp.names#template-argument.
- In a 动态异常规范 http://eel.is/c++draft/except.spec#dynamic-exception-specification ([除.spec http://eel.is/c++draft/except.spec]);该模式是type-id http://eel.is/c++draft/dcl.name#type-id.
- In an 属性列表 http://eel.is/c++draft/dcl.attr.grammar#attribute-list ([dcl.attr.语法 http://eel.is/c++draft/dcl.attr.grammar]);该模式是一个属性 http://eel.is/c++draft/dcl.attr.grammar#attribute.
- In an 对齐说明符 http://eel.is/c++draft/dcl.attr.grammar#alignment-specifier ([dcl.对齐 http://eel.is/c++draft/dcl.align]);模式是对齐说明符 http://eel.is/c++draft/dcl.attr.grammar#alignment-specifier没有省略号。
- In a 捕获列表 http://eel.is/c++draft/expr.prim.lambda#capture-list ([表达式.prim.lambda http://eel.is/c++draft/expr.prim.lambda]);该模式是capture http://eel.is/c++draft/expr.prim.lambda#capture.
- In a
sizeof...
表达 ([表达式.sizeof http://eel.is/c++draft/expr.sizeof]);该模式是一个标识符 http://eel.is/c++draft/lex.name#identifier.
- In a 折叠表达 http://eel.is/c++draft/expr.prim.fold#fold-expression ([表达式.prim.fold http://eel.is/c++draft/expr.prim.fold]);模式是强制转换表达式 http://eel.is/c++draft/expr.cast#cast-expression包含未扩展的参数包。
上面引用的标准草案谈论了什么语法的一部分链接中描述的是正在扩展的“模式”。要理解它,您需要了解 C++ 语法是如何描述的以及标准文本本身如何使用它的例外情况;但是,如果您有基本的 BNF 知识并且有一点耐心,您就可以解决这个问题。这些名称通常也很有用。
但是,您可以使用...
并且大多数人都明白它,但没有深入到那么深。
The general规则很简单:你有一些 C++ 语法(称为pattern)以通常的方式解析,其中一组类型被视为单个类型,一组文字被视为单个文字。然后,最后,你有一个...扩展器。然后,它会获取紧接之前的 C++ 语法位中的所有未展开包(pattern)并展开它。
它在每种情况下的工作原理是不同的;这不仅仅是宏观扩张。所处的背景...
上面列举的是有效的;标准中在每个有效点都列出了扩展的影响。
在最普通的用例中...
,该模式是一个表达式,并且该表达式被扩展,就好像它是由,
(not operator,
,但另一个“正常”的),通常在需要一系列事物的上下文中(函数调用、初始化列表等)。
有函数参数声明上下文(其中...
两者都扩展了函数参数的类型,并在模板参数列表中引入了新的参数名称包(通常在其中引入了参数包)等。
sizeof...
有点奇怪:对于sizeof...
它计算在包中传递的元素有多少个()
。这工作方式不同,因为...
不适用于“左侧结构”。
For alignas(X...)
,我们最终得到alignas(X@0), alignas(X@1), ...
(where @0
是我的包的第一个元素的伪代码),如alignas(X@0, X@1, ...)
不是有效的 C++(在下面的评论中再次@T.C.)。
对于继承,它创建一组基类。对于 mem-initializer-lists,它允许您将 ctor 参数传递给所述基类包。对于 lambda,它为您提供了有限的包捕获(我上次检查的扩展表达式捕获并不完整)。
模式是扩展的东西。重要的是,扩展模式不会被另一个模式扩展器扩展:所以std::array< Ts, sizeof...(Ts) >...
是一组不同类型的数组,每个数组的元素数量由数组本身的大小决定。sizeof...(Ts)
“算作扩展”Ts
in its ()
, 虽然...
不是“正确的”,因为语言定义了Ts
in the ()
作为扩展的模式...
.
一般情况下的模式不能称为表达式,因为类型不是表达式,而某些模式是表达式(或至少扩展为表达式列表)。并且在某些情况下,...
将类型模式扩展为类型的扩展包(就像在 throw 表达式中一样)。
一般规则是,你对待...
因为在本地上下文中以适当的方式扩展左边的东西,几乎适用于所有东西,除了sizeof...
(这是一个神奇的运算符,可以告诉您参数包中有多少个元素)。只有在无法产生像样模型的极端情况下才会出现这种情况。根据我的经验,最糟糕的是,它会导致代码在您认为应该编译时无法编译;在这种情况下,您可以学习解决方法。就像,记住一个简单的陈述“没有本地上下文”,所以你不能这样做a = std::get<Is>(tup))...;
,而是解决方法(void)(int[]){0,(a = std::get<Is>(tup)),0)...};
(可能必须输入int[]
),我们为包扩展提供一个上下文(创建一个数组)。
C++1z 的折叠表达式是另一个奇怪的地方,其中...
不适用于左侧;在这里,他们想要对二元运算符进行扩展,因此“在左侧”不像通常的“一元”扩展那样有意义。