I'm currently learning C++ with the book C++ Primer and one of the exercises in the book is:
Explain what the following expression does:
someValue ? ++x, ++y : --x, --y
What do we know? We know that the ternary operator has a higher precedence than the comma operator. With binary operators this was quite easy to understand, but with the ternary operator I am struggling a bit. With binary operators "having higher precedence" means that we can use parentheses around the expression with higher precedence and it will not change the execution.
For the ternary operator I would do:
(someValue ? ++x, ++y : --x, --y)
effectively resulting in the same code which does not help me in understanding how the compiler will group the code.
However, from testing with a C++ compiler I know that the expression compiles and I do not know what a :
operator could stand for by itself. So the compiler seems to interpret the ternary operator correctly.
Then I executed the program in two ways:
#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;
}
Results in:
11 10
While on the other hand with someValue = false
it prints:
9 9
Why would the C++ compiler generate code that for the true-branch of the ternary operator only increments x
, while for the false-branch of the ternary it decrements both x
and y
?
I even went as far as putting parentheses around the true-branch like this:
someValue ? (++x, ++y) : --x, --y;
but it still results in 11 10
.
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--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:
someValue?
(++x,++y)
or --x
(depending on bool
result 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
?
(someValue?(++x,++y):--x)
executes and increments x
and y
to be 11
and 11
--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