What's the Precedence of Comma Operator Inside Conditional Operator in C++

What's the precedence of comma operator inside conditional operator in C++?

The first one is equivalent to:

(true  ? (++x, ++y) : (--x)), --y; 

The second one is equivalent to:

(false ? (++x, ++y) : (--x)), --y; 

Thus the --y is always executed. In the first line, the increments are executed first so x = 1, y = 0 is expected. In the second line, the decrement of x is executed first so x = -1, y = -1 is expected.


As noted in a comment (to another answer) by Barmar:

And in case anyone is wondering why the comma between ++x and ++y doesn't have the same effect, it's because (true? ++x) would not be valid at all. So the compiler keeps scanning until it finds the :, but beyond that it stops when it reaches a lower precedence operator [(, in this example) or the end of statement].

Ternary and comma operator

Due to operator precedence (comma operator having least precedence), your code actually looks like

int a,b;
(a = 1 ? (1,2) : 3),4; // a = 2
(b = 0 ? (1,2) : 3),4; // b = 3

So, as per the ternary condition rule, quoting C11, chapter §6.5.15

The first operand is evaluated; there is a sequence point between its evaluation and the
evaluation of the second or third operand (whichever is evaluated). The second operand
is evaluated only if the first compares unequal to 0; the third operand is evaluated only if
the first compares equal to 0; the result is the value of the second or third operand
(whichever is evaluated), converted to the type described below. 110)

[...]

110) A conditional expression does not yield an lvalue.

  • For first case, the second operand is evaluated and returned.
  • For second case, the third operand is evaluated and returned.

Comma operator precedence while used with ? : operator

This one:

(a > b)? a++,b-- : a--,b++; 

is equivalent to:

((a > b) ? (a++, b--) : a--), b++;

so b is always incremented and only sometimes decremented. There is no way to parse the comma operator between ? and : other than as parenthesized in the 'equivalent to' expression. But after the :, the unparenthesized comma terminates the ternary ?: operator and leaves the increment as unconditionally executed. The precedence of the comma operator is very, very low.

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

As @Rakete said in their excellent answer, this is tricky. I'd like to add on to that a little.

The ternary operator must have the form:

logical-or-expression ? expression : assignment-expression

So we have the following mappings:

  • someValue : logical-or-expression
  • ++x, ++y : expression
  • ??? is assignment-expression --x, --y or only --x?

In fact it is only --x because an assignment expression cannot be parsed as two expressions separated by a comma (according to C++'s grammar rules), so --x, --y cannot be treated as an assignment expression.

Which results in the ternary (conditional) expression portion to look like this:

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

It may help for readability's sake to consider ++x,++y to be computed as-if parenthesized (++x,++y); anything contained between ? and : will be sequenced after the conditional. (I'll parenthesize them for the rest of the post).

and evaluated in this order:

  1. someValue?
  2. (++x,++y) or --x (depending on boolresult of 1.)

This expression is then treated as the left sub-expression to a comma operator, with the right sub-expression being --y, like so:

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

Which means the left side is a discarded-value expression, meaning that it is definitely evaluated, but then we evaluate the right side and return that.

So what happens when someValue is true?

  1. (someValue?(++x,++y):--x) executes and increments x and y to be 11 and 11
  2. The left expression is discarded (though the side effects of increment remain)
  3. We evaluate the right hand side of the comma operator: --y, which then decrements y back to 10

To "fix" the behavior, you can group --x, --y with parentheses to transform it into a primary expression which is a valid entry for an assignment-expression*:

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

*It's a rather funny long chain that connects an assignment-expression back to a primary expression:

assignment-expression ---(can consist of)--> 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

Precedence of the C++ Comma Operator

This is not about precedence; it's about order of evaluation. The comma operator, unlike most other C++ operators (pre-C++17), strictly enforces a specific order of evaluation. Comma expressions are always evaluated left-to-right.

c++ expression value (operator precedence)

According to operator precedence,

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

is parsed as

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

Variable assignments in C with comma operator

You already found out that = has higher precedence.
Therefore that first code is similar to

(x = 1),2, 3;

That means you have an assignment and you have 2 values combined by comma operator.

The assignment is evaluated to 1 (besides actually assigning a value to x) and you get an expression like this: 1,2,3; which is evaluated but ignored.

In your second snippet things are different.

int x =1, 2, 3;

Again the precedence is same.

If you add brackets int x = (1,2,3); you will get value 3 as you expected.

Without the brackets, the compiler expects the name of the next variable that you want to define here.
2 and 3 are no valid identifiers to define variables and therefore the compiler complains.

Comma operator in C++11 (sequencing)

When you see

 expression:
assignment-expression
expression, assignment-expression

It mean that there are 2 possibilities for expression. One possibility that it is just assignment-expression that is defined somewhere earlier. Or it is recursively represented as expression, assignment-expression

So after extending it you receive that expression is comma separated list of one or more assignment-expression tokens.

In the sample you're mentioned second parameter is expression (t=3,t+2) which consists of 2 comma-separated assignment-expressions - and since it appears "In contexts where comma is given a special meaning" it has to "appear only in parentheses".

To find out why assignment-expression could take a form of t+2 you have to go back from its definitions and choose first choice always

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


Related Topics



Leave a reply



Submit