关于c ++:为什么带逗号的三元运算符在真实情况下只评估一个表达式?

Why does the ternary operator with commas evaluate only one expression in the true case?

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

Explain what the following expression does: someValue ? ++x, ++y : --x, --y

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

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

1
(someValue ? ++x, ++y : --x, --y)

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
#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;
}

结果是:

1
11 10

另一方面,用someValue = false打印:

1
9 9

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

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

1
someValue ? (++x, ++y) : --x, --y;

但它仍然导致11 10


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

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

logical-or-expression ? expression : assignment-expression

所以我们有以下映射:

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

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

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

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

为了可读性,可能有助于将++x,++y计算为 - 如果括号(++x,++y);在?:之间包含的任何内容都将在条件之后排序。 (我会在帖子的其余部分将它们括起来)。

并按此顺序评估:

  • someValue?
  • (++x,++y)--x(取决于bool 1的结果。)
  • 然后将此表达式视为逗号运算符的左子表达式,右子表达式为--y,如下所示:

    1
    (someValue?(++x,++y):--x), --y;

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

    那么当someValue true时会发生什么?

  • (someValue?(++x,++y):--x)执行并将xy增量为1111
  • 左表达式被丢弃(尽管增量的副作用仍然存在)
  • 我们评估逗号运算符的右侧:--y,然后将y递减回10
  • 要"修复"该行为,可以将--x, --y与括号分组,将其转换为主表达式,该表达式是赋值表达式*的有效条目:

    1
    someValue?++x,++y:(--x, --y);

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

    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


    哇,这太棘手了。

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

    1
    (someValue ? (++x, ++y) : --x), --y;

    三元运算符需要一个:,它不能在该上下文中独立存在,但在它之后,没有理由为什么逗号应该属于错误情况。

    现在,为什么你得到那个输出可能更有意义。如果someValue为真,则执行++x++y--y,这不会有效地改变y,但会将<1>添加到x

    如果someValue为false,则执行--x--y,将它们减1。


    Why would the C++ compiler generate code that for the true-branch of the ternary operator only increments x

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

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

    1
    2
       (someValue ? ++x, ++y : --x), (--y);
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^

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

    I even went as far as putting parentheses around the true-branch like this:

    1
    someValue ? (++x, ++y) : --x, --y;

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

    1
    someValue ? ++x, ++y : (--x, --y);

    演示(打印11 11)


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

    1
    a ? b++, c++ : d++

    被视为:

    1
    a ? (b++, c++) : d++

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

    1
    a ? b++ : c++, d++

    被视为:

    1
    (a ? b++ : c++), d++

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


    在答案中被忽略的一点(虽然涉及到注释)是条件运算符总是在实际代码中用于(设计意图?)作为将两个值中的一个赋值给变量的快捷方式。

    因此,更大的背景是:

    1
    whatIreallyWanted = someValue ? ++x, ++y : --x, --y;

    这是荒谬的,所以犯罪是多方面的:

    • 该语言允许在作业中产生荒谬的副作用。
    • 编译器没有警告你,你在做奇怪的事情。
    • 这本书似乎专注于"技巧"问题。人们只能希望后面的答案是"这个表达的作用取决于一个人为的例子中的奇怪的边缘情况,以产生没人想要的副作用。永远不要这样做。"