定义"模式"用于参数包扩展,尤其是在函数调用

时间:2016-05-02 13:54:51

标签: c++ c++11

据我所知,当包含参数包的模式右侧出现省略号(...)时,模式会对包中的每个参数展开一次。然而,尽管我已经能够通过扩展找到模式的孤立示例,但我无法找到模式构成的定义。从我所看到的,空格在模式的定义中没有任何作用,但是括号有。例如,在此示例中:

template<typename ... Ts>
void func(Ts)
{
    do_something(validate(Ts)...);
}

do_something行将扩展为:

    do_something(validate(var1), validate(var2), validate(var3))

如果Ts碰巧代表三个变量。相比之下:

    do_something(validate(Ts...));

将扩展为:

    do_something(validate(var1, var2, var3));

因此,明确括号与确定模式的开始和结束位置有关。我也可以看到空白没有。但这只能让我到目前为止。我想确切地知道什么构成了一个模式,以及它将如何扩展。我尝试搜索C ++标准,但发现太多的&#34;参数包&#34;使这有效。有人可以给我一个&#34;模式&#34;的定义,或定义的链接,或两者兼而有之?

更新:为了限制我的问题范围,我想关注函数调用中出现模式的情况。我已经相应地编辑了标题。对不起,我从一开始就没有说清楚。

2 个答案:

答案 0 :(得分:7)

模式[temp.variadic]/4下的标准中定义:(通过@t.c.

  

pack扩展模式和省略号组成,其实例化在列表中生成零个或多个模式的实例化(如下所述)。模式的形式取决于扩展发生的环境。包扩展可以在以下上下文中发生:

     

标准草案中的上述引言说明链接中描述的部分语法是&#34;模式&#34;正在扩大。要理解它,您需要知道如何描述C ++语法以及有关如何在标准文本本身中使用它的例外情况;但是,如果你有基本的BNF知识和一点耐心,你可以解决它。这些名字通常也很有用。

但是,您可以使用...并且大多数情况下都不会理解它。

通用规则很简单:你有一些C ++语法(称为模式)以通常的方式解析,其中一组类型被视为一个类似单一类型,一包文字被视为单个文字。然后,在它结束时,你有一个 ... 扩展器。然后它会立即将所有未扩展的包装在C ++语法中( pattern )并展开它。

这在每个上下文中的工作原理是不同;它不仅仅是一次宏观扩张。上面列举了...有效的上下文;扩展的影响在有效的每个点都列在标准中。

...的最平凡的用例中,模式是一个表达式,如果每个副本由,(不是operator,分隔,则表达式会被展开,但是另一个&#34; normal&#34; one),通常在预期事物列表的上下文中(函数调用,初始化列表等)。

有函数参数声明上下文(其中...都扩展了函数参数的类型,并引入了一个参数名称的新包),在模板参数列表中(通常引入包),等等

sizeof...有点奇怪:对于sizeof...,它计算()中传递的包中有多少元素。这样做的方式不同,因为...不适用于左边的&#34;结构&#34;。

对于alignas(X...),我们最终得到alignas(X@0), alignas(X@1), ...(其中@0是我的第一个元素的伪代码),因为alignas(X@0, X@1, ...)无效C ++(再次@TC在下面的评论中。)

对于继承,它创建一组基类。对于mem-initializer-lists,它允许您将ctor参数传递给所述基类包。对于lambda,它会为您提供有限的包捕获(在我检查的扩展时,表达捕获并不完整)。

模式是扩展的东西。重要的是,扩展模式不会被另一个模式扩展器扩展:因此std::array< Ts, sizeof...(Ts) >...是一组各种类型的数组,每个数组都有一些元素,这些元素由包本身的大小决定。 sizeof...(Ts)&#34;算作扩展&#34;其Ts中的(),即使...不是&#34;其权利&#34;,因为该语言定义了Ts {1}}作为由()扩展的模式。

一般情况下的模式不能称为表达式,因为类型不是表达式,而某些模式是表达式(或至少扩展为表达式列表)。在某些情况下,...将类型模式扩展为扩展的类型包(如throw表达式中)。

一般规则,你将...视为在本地环境中以适当的方式扩展左边的东西,适用于除...之外的几乎所有东西(这是一个神奇的操作符告诉你参数包中有多少个元素)。只是在角落的情况下才会产生一个不错的模型。根据我的经验,更糟糕的是它会导致代码在您认为应该编译时无法编译;在这种情况下,您可以学习解决方法。就像,记住一个简单的声明&#34;没有本地环境&#34;,所以你不能sizeof...,而是解决方法a = std::get<Is>(tup))...;(可能必须键入dede (void)(int[]){0,(a = std::get<Is>(tup)),0)...};)我们为包扩展提供了一个上下文(创建一个数组)。

C ++ 1z的折叠表达式是另一个奇怪的地方,int[]不适用于左边;在这里,他们希望扩展一个二元运算符,所以&#34;在左边&#34;并不像通常的&#34;一元&#34;那样有意义。扩展

答案 1 :(得分:0)

模式由与...扩展语法的关联定义。所以validate(ts)仅仅是无意义文本的片段。但是validate(ts)...使validate(ts)部分成为扩展的模式。因此,...如何影响相关代​​码。

通常,解包语法指定...左侧的内容是模式。使用C ++ 17的折叠表达式,这变得有点复杂。但一般来说,如果你想知道模式是什么,找到...,然后立即查看左边的表达式。

模式中可能存在的具体限制完全取决于扩展的位置以及包含的类型(类型与非类型等)。该模式必须是任何合法代码,无论它在何种环境中被扩展。