C++11 中的逗号运算符(排序)

Comma operator in C++11 (sequencing)

标准提到 f(a,(t=3,t+2),c); 这将是一个赋值表达式,后跟第二个运算符的表达式据我了解。

但语法列出它并列:

表达式:

赋值表达式

表达式,赋值表达式

工作草案,编程标准 C语言 ++ 修订版 N4140(2014 年 11 月)

有人好心向我解释一下我在这里缺少什么吗?

这是语法符号(参见 N4140 的 §1.6)。

主要用于判断优先级,但名字容易产生误导。

例如,在 [expr.ass] (§5.18) 中,您有以下定义:

assignment-expression:
   conditional-expression
   logical-or-expression assignment-operator initializer-clause
   throw-expression

assignment-operator: one of
   = *= /= %= += -= >>= <<= &= ^= |=

因此 assignment-expression 可以是 conditional-expressionthrow-expression,即使两者都不执行任何赋值。

这只是说明 a = bthrow 10cond ? c : d 是具有相同优先顺序的表达式。

当你看到

 expression:
    assignment-expression
    expression, assignment-expression

表示有2种可能的表达方式。一种可能性是 assignment-expression 是在较早的某处定义的。或者递归表示为expression, assignment-expression

因此在扩展它之后,您会收到该表达式是一个或多个赋值表达式标记的逗号分隔列表。

在您提到的示例中,第二个参数是表达式 (t=3,t+2),它由 2 个逗号分隔的赋值表达式组成 - 因为它出现 "In contexts where comma is given a special meaning",所以它必须 "appear only in parentheses".

要找出为什么赋值表达式可以采用 t+2 的形式,您必须返回它的定义并始终选择第一个选择

assignment-expression
-> conditional-expression
--> logical-or-expression
---> logical-and-expression
----> inclusive-or-expression
-----> exclusive-or-expression
------> and-expression
-------> equality-expression
--------> relational-expression
---------> shift-expression
----------> additive-expression - this is what you see
 f(a,(t=3,t+2),c);

这里,首先将3存入t变量,然后用三个参数调用函数f()。这意味着第二个参数值变为 5 并传递给函数。

注意因为表达式的定义是

expression:

assignment-expression

expression , assignment-expression

第二行意味着任何assignment-expression都可以被认为是一个expression,这就是为什么t=3, t+2是一个有效表达式。

那么为什么语法是这样的呢?首先请注意,表达式的语法是从最紧密绑定的类别 primary-expression 到最不紧密绑定的类别 expression 逐步构建的。 (然后“( expression )”是一个 primary-expression 的事实使表达式语法完整循环并让我们通过添加括号使任何表达式比围绕它的所有内容更紧密地绑定。)

例如,众所周知的事实是二进制 * 比二进制 + 绑定得更紧密,这些语法片段可得出以下结论:

multiplicative-expression:

pm-expression

multiplicative-expression * pm-expression

multiplicative-expression / pm-expression

multiplicative-expression % pm-expression

additive-expression:

multiplicative-expression

additive-expression + multiplicative-expression

additive-expression - multiplicative-expression

在表达式2 + 3 * 4中,文字234可以被认为是一个pm-expression,或者因此也是一个 乘法表达式 加法表达式 。所以你可能会说 2 + 3 符合 加法表达式 ,但它 不是 一个 乘法表达式,所以完整的 2 + 3 * 4 不能那样工作。相反,语法强制 3 * 4 被视为 乘法表达式 ,因此 2 + 3 * 4 可以是 加法表达式 .因此 3 * 4 是二进制 +.

的子表达式

或者在表达式 2 * 3 + 4 中,3 + 4 可能被认为是一个 加法表达式 ,但它不是 pm -expression,所以这不起作用。相反,解析器必须识别 2 * 3 是一个 乘法表达式 ,它也是一个 加法表达式 ,因此 2 * 3 + 4 是一个有效的 加法表达式 ,其中 2 * 3 作为二进制 +.

的子表达式

当同一个运算符被使用两次,或者使用两个具有相同优先级的运算符时,大多数语法定义的递归性质很重要。

回到逗号语法,如果我们有标记“a, b, c”,我们可能会说 b, c 可能是一个 表达式 ,但它不是 赋值表达式,因此 b, c 不能是整体的子表达式。相反,语法要求将 a, b 识别为 表达式 ,它被允许作为另一个逗号运算符的左子表达式,因此 a, b, c 也是一个 表达式,左操作数为a, b

这对于内置逗号没有任何区别,因为它的含义是关联的:“评估并丢弃a,然后结果值来自评估(评估并丢弃b, then the result value comes from evaluation c)" 与 "evaluate and discard (evaluate and discard a, then result value comes from evaluation b), then then then结果值来自评估 c".

但在重载 operator, 的情况下,它确实为我们提供了明确定义的行为。鉴于:

struct X {};
X operator,(X, X);
X a, b, c;
X d = (a, b, c);

我们知道最后一行的意思是

X d = operator,(operator,(a,b), c);

而不是

X d = operator,(a, operator,(b,c));

(我认为定义一个非关联的operator,特别邪恶,但这是允许的。)