为什么带逗号的三元运算符仅在真实情况下评估一个表达式?

Auf*_*gel 118 c++ conditional-operator operator-precedence language-lawyer

我目前正在学习使用C++ Primer一书的C++,本书的其中一个练习是:

解释以下表达式的作用: someValue ? ++x, ++y : --x, --y

我们知道什么?我们知道三元运算符的优先级高于逗号运算符.使用二元运算符这很容易理解,但是对于三元运算符,我有点挣扎.使用二元运算符"具有更高的优先级"意味着我们可以使用具有更高优先级的表达式周围的括号,并且它不会更改执行.

对于三元运算符,我会这样做:

(someValue ? ++x, ++y : --x, --y)
Run Code Online (Sandbox Code Playgroud)

有效地产生相同的代码,这无法帮助我理解编译器如何对代码进行分组.

但是,通过使用C++编译器进行测试,我知道表达式编译并且我不知道:运算符本身可以代表什么.所以编译器似乎正确地解释了三元运算符.

然后我以两种方式执行程序:

#include <iostream>

int main()
{
    bool someValue = true;
    int x = 10, y = 10;

    someValue ? ++x, ++y : --x, --y;

    std::cout << x << " " << y << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果是:

11 10
Run Code Online (Sandbox Code Playgroud)

而另一方面someValue = false它与它打印:

9 9
Run Code Online (Sandbox Code Playgroud)

为什么C++编译器生成的代码只能为三元运算符的真分支递增x,而对于三元的假分支,它会减少xy

我甚至把括号括在真正的分支周围,就像这样:

someValue ? (++x, ++y) : --x, --y;
Run Code Online (Sandbox Code Playgroud)

但它仍然会导致11 10.

And*_*dyG 121

正如@Rakete在他们出色的回答中所说,这很棘手.我想补充一点.

三元运算符必须具有以下形式:

逻辑或表达式 ? 表达式 : 赋值表达式

所以我们有以下映射:

  • someValue:logical-or-expression
  • ++x, ++y:表达
  • ??? 是赋值表达 --x, --y还是仅--x

实际上,这只是--x因为赋值表达式不能被解析为用逗号分隔的两个表达式(根据C++的语法规则),因此--x, --y不能将其视为赋值表达式.

这导致三元(条件)表达式部分看起来像这样:

someValue?++x,++y:--x
Run Code Online (Sandbox Code Playgroud)

它可以帮助提高可读性的缘故考虑++x,++y到计算的,如果括号(++x,++y); 包含之间的任何东西?:将被测序的条件.(我会在帖子的其余部分将它们括起来).

并按此顺序评估:

  1. someValue?
  2. (++x,++y)--x(取决于bool1的结果)

然后将此表达式视为逗号运算符的左子表达式,右子表达式--y如下:

(someValue?(++x,++y):--x), --y;
Run Code Online (Sandbox Code Playgroud)

这意味着左侧是一个丢弃值表达式,意味着它被明确地评估,但是然后我们评估右侧并返回它.

当会发生什么someValuetrue

  1. (someValue?(++x,++y):--x)执行和增加xy成为1111
  2. 左表达式被丢弃(尽管增量的副作用仍然存在)
  3. 我们评估逗号运算符的右侧:--y然后递减y回来10

要"修复"该行为,可以--x, --y使用括号进行分组,将其转换为主表达式,该表达式赋值表达式*的有效条目:

someValue?++x,++y:(--x, --y);
Run Code Online (Sandbox Code Playgroud)

*这是一个相当有趣的长链,它将赋值表达式连接回主表达式:

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 - > multiplicative-expression - > pm-expression - > cast-expression - > unary-expression - > postfix-expression - > primary-expression

  • 感谢您解开语法规则; 这样做表明,与大多数教科书相比,C++的语法更多. (10认同)
  • @sdenham:当人们问为什么"面向表达式的语言"很好时(即当`{...}`可以被视为表达式时),我现在有一个答案=>这是为了避免引入一个逗号运算符以这种棘手的方式. (3认同)

Rak*_*111 87

哇,这太棘手了.

编译器将您的表达式视为:

(someValue ? (++x, ++y) : --x), --y;
Run Code Online (Sandbox Code Playgroud)

三元运算符需要a :,它在该上下文中不能独立,但在它之后,没有理由为什么逗号应该属于false case.

现在,为什么你得到那个输出可能更有意义.如果someValue是,那么++x,++y--y执行,这不会有效地改变,y但添加一个x.

如果someValue是假的,那么--x--y执行,由一个递减他们.


das*_*ght 42

为什么C++编译器会生成三元运算符的真分支仅递增的代码 x

你误解了发生了什么.真正的分支增加xy.但是,y在此之后立即无条件地减少.

以下是这种情况:由于条件运算符的优先级高于C++中的逗号运算符,编译器将按如下方式解析表达式:

   (someValue ? ++x, ++y : --x), (--y);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^
Run Code Online (Sandbox Code Playgroud)

注意--y逗号后面的"孤立" .这是导致y最初递增的递减的原因.

我甚至把括号括在真正的分支周围,就像这样:

someValue ? (++x, ++y) : --x, --y;
Run Code Online (Sandbox Code Playgroud)

你是在正确的道路上,但你把一个错误的分支括起来:你可以通过括号化else分支来解决这个问题,如下所示:

someValue ? ++x, ++y : (--x, --y);
Run Code Online (Sandbox Code Playgroud)

演示(打印11 11)


Mar*_*ica 5

你的问题是三元表达式的优先级并不高于逗号.实际上,C++不能简单地通过优先级来准确描述 - 它正是三元运算符和逗号分解之间的交互.

a ? b++, c++ : d++
Run Code Online (Sandbox Code Playgroud)

被视为:

a ? (b++, c++) : d++
Run Code Online (Sandbox Code Playgroud)

(逗号的行为就像它具有更高的优先级).另一方面,

a ? b++ : c++, d++
Run Code Online (Sandbox Code Playgroud)

被视为:

(a ? b++ : c++), d++
Run Code Online (Sandbox Code Playgroud)

并且三元运算符具有更高的优先级.