“模式”的定义包括:用于参数包扩展,尤其是在函数调用中 [英] Definition of "pattern" for parameter pack expansion, especially within a function call

查看:54
本文介绍了“模式”的定义包括:用于参数包扩展,尤其是在函数调用中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我了解到,在包含参数包的模式的右侧出现省略号(...)时,该模式将针对该包中的每个参数扩展一次。但是,尽管我能够找到扩展了模式的孤立示例,但无法找到组成模式的定义。从我所看到的,空格在模式的定义中不起作用,但是括号起作用。例如,在此示例中:

I understand that when an ellipsis (...) occurs to the right of a pattern containing a parameter pack, the pattern is expanded once for each parameter in the pack. However, though I have been able to find isolated examples of patterns with their expansion, I have been unable to find a definition of what constitutes a pattern. From what I can see, whitespace plays no role in the definition of the pattern, but parentheses do. For instance, in this example:

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

do_something 行将会扩展为:

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

如果 Ts 恰好代表三个变量。相比之下:

if Ts happened to represent three variables. By contrast:

    do_something(validate(Ts...));

将扩展为:

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

显然,括号与确定模式的开始和结束位置有关。我还可以看到空格没有。但这只能使我走远。我想确切地知道什么是模式的组成,以及它将如何扩展。我尝试搜索C ++标准,但是发现太多的参数包实例无法生效。有人可以给我一个模式的定义,或者一个定义的链接,或者两者都给吗?

So clearly parentheses have something to do with determining where the pattern begins and ends. I can also see that whitespace does not. But that only gets me so far. I'd like to know exactly what constitutes a pattern, and how it will be expanded. I tried searching through the C++ Standard, but found too many instances of "parameter pack" to make that effective. Could someone please give me a definition of "pattern", or a link to a definition, or both?

更新:为了限制我的问题的范围,我想我希望重点关注在函数调用中出现模式的情况。我已经相应地编辑了标题。抱歉,我从一开始就不清楚。

UPDATE: To limit the scope of my question, I'd like to focus on the case where a pattern occurs within a function call. I've edited the title accordingly. Sorry I didn't make that clear from the beginning.

推荐答案

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

A pattern is defined in the standard under [temp.variadic]/4: (via @t.c.)


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

A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:


  • 在函数参数包中([ dcl.fct ]));该模式是 parameter-declaration

  • 在作为包扩展的模板参数包中([ temp.param ]):

    • In a function parameter pack ([dcl.fct]); the pattern is the parameter-declaration without the ellipsis.
    • In a template parameter pack that is a pack expansion ([temp.param]):
      • if the template parameter pack is a parameter-declaration; the pattern is the parameter-declaration without the ellipsis;
      • if the template parameter pack is a type-parameter with a template-parameter-list; the pattern is the corresponding type-parameter without the ellipsis.

      以上标准草稿中的引言是关于语法的什么部分 是正在扩展的模式。要理解它,您需要知道如何描述C ++语法以及在标准文本本身中如何使用C ++语法的例外情况。但是,如果您具有基本的BNF知识并有一点耐心,则可以解决。名称通常也很有用。

      The above quote from the standard draft talks about what part of the grammar described in the links is the "pattern" that is being expanded. To understand it, you need to know how the C++ grammar is described and the exceptions about how it is used in the standard text itself; however, if you have basic BNF knowledge and a bit of patience, you can work it out. The names are often useful as well.

      但是,您可以使用 ... 并几乎不加深入地了解它。

      However, you can use ... and mostly understand it without going nearly that deep.

      通用规则很简单:您有一些C ++语法(称为 pattern ),以通常的方式进行解析,其中将一堆类型视为单个类型,将一堆文字视为单个文字。然后,在它的结尾,您有一个 ... 扩展器。然后,它将紧接( pattern )之前的C ++语法中的所有未扩展包打包并展开。

      The general rule is simple: you have some bit of C++ grammar (called the pattern) parsed in the usual way, where a pack of types is treated like a single type, and a pack of literals is treated like a single literal. Then, at the end of it, you have a ... expander. It then takes all of the unexpanded packs in the bit of C++ grammar immediately before (the pattern) and expands it.

      每个上下文都是不同;这不仅仅是宏扩展。上面列举了 ... 有效的上下文;

      How this works in each context is different; it isn't just a macro expansion. The contexts in which ... is valid are enumerated above; the effects of the expansion are listed in the standard at each point where it is valid.

      在最普通的用例 ... ,该模式是一个表达式,并且如果每个副本均由分隔(而不是运算符),则表达式将按以下方式扩展: ,,但另一个正常),通常是在需要事物列表(函数调用,初始化列表等)的情况下。

      In the most mundane use cases of ..., the pattern is an expression, and the expression gets expanded as-if it was each copy separated by , (not operator,, but the other "normal" one), usually in a context where a list of things is expected (function call, initializer list, etc).

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

      There are function parameter declaration contexts (where ... both expands the types of the function parameters, and introduces a new pack of the paramter names), in template parameter lists (where it introduces a pack usually), etc.

      sizeof ... 有点奇怪:对于 sizeof ... ,它计算在()中传递的包中有多少个元素。效果不同,因为 ... 不适用于左侧结构。

      sizeof... is a bit strange: for sizeof... it counts how many elements are in the pack passed in the (). This works differently, as the ... does not apply to the "structure on the left".

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

      For alignas(X...), we end up with alignas(X@0), alignas(X@1), ... (where @0 is my pseudocode for the first element of the pack), as alignas(X@0, X@1, ...) is not valid C++ (again @T.C. in comments below).

      对于继承,它将创建一组基类。对于mem-initializer-lists,它使您可以将ctor参数传递给所说的基类包。对于lambda,它为您提供了有限的包捕获(在我最后一次检查扩展时不能完全捕获表达式捕获)。

      For inheritance, it creates a set of base classes. For mem-initializer-lists, it lets you pass ctor arguments to said pack of base classes. For lambdas, it gives you limited capture of packs (not full on expression capture with expansion last I checked).

      模式是扩展的东西。重要的是,扩展模式不会被另一个模式扩展器扩展:因此 std :: array< Ts,sizeof ...(Ts)> ... 是一包各种类型的数组,每个数组都有许多元素,这些元素由包本身的大小决定。 sizeof ...(Ts)在其()中将 Ts 视为扩展 ,即使 ... 并非正确,因为该语言定义了 Ts ()中的c $ c>作为由 ... 扩展的模式。

      The pattern is the thing expanded. Importantly, an expanded pattern is not expanded by another pattern expander: so std::array< Ts, sizeof...(Ts) >... is a pack of arrays of various types, each one with a number of elements determined by how big the pack itself is. sizeof...(Ts) "counts as expanding" the Ts in its (), even though ... isn't to "its right", because the language defines the Ts in the () as the pattern that is expanded by the ....

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

      The pattern in the general case cannot be called an expression, because types are not expressions, and some patterns are expressions (or at least expand into lists of expressions). And in some cases, ... expands a type-pattern into an expanded package of types (like in the throw expression).

      一般规则是,您将 ... 视为在本地情况下以适当的方式扩展左侧的内容,除 sizeof ... (这是一个神奇的运算符,它告诉您参数包中有多少个元素)。只有在极端情况下,这不能产生一个不错的模型。以我的经验,更糟糕的是,它会导致代码在您认为应该编译时无法编译;您可以在这种情况下了解解决方法。就像,记住一条裸露的语句没有本地上下文,所以您不能执行 a = std :: get< Is>(tup))...; ,但是而是解决方法(void)(int []){0,(a = std :: get< Is>(tup)),0)...}; (可能必须键入 int [] )的类型,在其中我们提供一个上下文(创建数组)以使包扩展起作用。

      The general rule, that you treat ... as expanding the thing on the left in an appropriate way in the local context, works for almost everything except sizeof... (which is a magical operator that tells you how many elements are in a parameter pack). It is only going to be in corner cases where this doesn't produce a decent model. And in my experience, at worse it will result in code that doesn't compile when you think it should; you can learn workarounds in that case. Like, remembering that a bare statement "has no local context", so you cannot do a = std::get<Is>(tup))...;, but rather the workaround (void)(int[]){0,(a = std::get<Is>(tup)),0)...}; (might have to typedef that int[]) where we provide a context (creating an array) for the pack expansion to work in.

      C ++ 1z的折叠表达式是另一个奇怪的地方,其中 ... 在左边不适用;在这里,他们想扩展二进制运算符,所以在左边没有通常的一元扩展意义。

      C++1z's fold expressions are another quirky spot where ... does not apply on the left; here, they wanted to have an expansion of a binary operator, so "on the left" doesn't make as much sense as the usual "unary" expansion.

      这篇关于“模式”的定义包括:用于参数包扩展,尤其是在函数调用中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆