Something we found when using comma in condition ternary operator? [duplicate] - c++

This question already has answers here:
What's the precedence of comma operator inside conditional operator in C++?
(3 answers)
Closed 9 years ago.
Well, I had a question about comma in ternary operator.
Cut the crap, the code is below:
void test_comma_in_condition(void)
{
int ia, ib, ic;
ia = ib = ic = 0;
bool condition=true;
cout<<"Original:"<<endl;
cout<<"ia: "<<ia<<endl;
cout<<"ib: "<<ib<<endl;
condition?(ia=1, ib=2):(ia=11, ib=12);
cout<<"After:"<<endl;
cout<<"ia: "<<ia<<endl;
cout<<"ib: "<<ib<<endl;
ia = ib = ic = 0;
condition?ia=1, ib=2, ic=3:ib=22,ia=21, ic=23;
cout<<"The operation must be bracketed, or you'll see..."<<endl;
cout<<"ia: "<<ia<<endl;
cout<<"ib: "<<ib<<endl;
cout<<"ic: "<<ic<<endl;
condition?ia=1, ib=2, ic=3:ia=21, ib=22, ic=23;
cout<<"The operation must be bracketed, or you'll see..."<<endl;
cout<<"ia: "<<ia<<endl;
cout<<"ib: "<<ib<<endl;
cout<<"ic: "<<ic<<endl;
return;
}
The output will be like:
Original:
ia: 0
ib: 0
After:
ia: 1
ib: 2
The operation must be bracketed, or you'll see...
ia: 21
ib: 2
ic: 23
The operation must be bracketed, or you'll see...
ia: 1
ib: 22
ic: 23
Is this legal?

This is a matter of operator precedence. Your expression:
condition?ia=1, ib=2, ic=3:ib=22,ia=21, ic=23;
is understood by the compiler as:
(condition?(ia=1, ib=2, ic=3):(ib=22)),ia=21, ic=23;
At this point you should be able to see why you get the program output.

Yes, the relevant grammar for a conditional expression is:
logical-or-expression ? expression : assignment-expression
for assignment expressions (which can also be a conditional-expression or a throw-expression):
logical-or-expression assignment-operator assignment-expression
and for an expression with a comma operator (an assignment-expression can also be an expression):
expression , assignment-expression
This means that the construct a ? b : c, d cannot be parsed as equivalent to a ? b : (c, d) because c, d is not an assignment-expression but must be parsed as equivalent to (a ? b : c), d.
There is no undefined behaviour in condition ? ia=1,ib=2,ic=3 : ia=21, ib=22, ic=23; because evaluation of condition is sequenced before the evaluation of either the second or third operands of ?: and in every sub-expression containing a comma operator the evaluation of the first operand of the comma operator is sequenced before the evaluation of the second operand.

It's legal, but stupid not very useful to write code like that.
The code
condition?ia=1, ib=2, ic=3:ia=21, ib=22, ic=23;
is equivalent to
condition?(ia=1, ib=2, ic=3):ia=21;
ib=22;
ic=23;
just harder to read.

The problem is that the comma operator has the lowest precedence there is. Thanks to that, the else part of the conditional operator is just the first assignment, after that the comma operator kicks in and the other two statements will be executed just aswell.

Related

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

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
??? 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:
someValue?
(++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?
(someValue?(++x,++y):--x) executes and increments x and y to be 11 and 11
The left expression is discarded (though the side effects of increment remain)
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
Wow, that's tricky.
The compiler sees your expression as:
(someValue ? (++x, ++y) : --x), --y;
The ternary operator needs a :, it cannot stand by itself in that context, but after it, there is no reason why the comma should belong to the false case.
Now it might make more sense why you get that output. If someValue is true, then ++x, ++y and --y get executed, which doesn't effectively change y but adds one to x.
If someValue is false, then --x and --y are executed, decrementing them both by one.
Why would the C++ compiler generate code that for the true-branch of the ternary operator only increments x
You misinterpreted what has happened. The true-branch increments both x and y. However, y is decremented immediately after that, unconditionally.
Here is how this happens: since the conditional operator has higher precedence than comma operator in C++, the compiler parses the expression as follows:
(someValue ? ++x, ++y : --x), (--y);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^
Note the "orphaned" --y after the comma. This is what leads to decrementing y that has been initially incremented.
I even went as far as putting parentheses around the true-branch like this:
someValue ? (++x, ++y) : --x, --y;
You were on the right path, but you parenthesized a wrong branch: you can fix this by parenthesizing the else-branch, like this:
someValue ? ++x, ++y : (--x, --y);
Demo (prints 11 11)
Your problem is that the ternary expression doesn't really have higher precedence than comma. In fact, C++ can't be described accurately simply by precedence - and it is exactly the interaction between the ternary operator and comma where it breaks down.
a ? b++, c++ : d++
is treated as:
a ? (b++, c++) : d++
(comma behaves as if it has higher precedence). On the other hand,
a ? b++ : c++, d++
is treated as:
(a ? b++ : c++), d++
and the ternary operator is higher precedence.
A point that's been overlooked in answers (though touched on comments) is that the conditional operator is invariably used (intended by design?) in real code as a shortcut for assigning one of two values to a variable.
So, the larger context would be:
whatIreallyWanted = someValue ? ++x, ++y : --x, --y;
Which is absurd on its face, so the crimes are manifold:
The language permits ridiculous side effects in an assignment.
The compiler didn't warn you that you were doing bizarre things.
The book appears to be focusing on 'trick' questions. One can only hope that the answer in the back was "What this expression does is depend on weird edge cases in a contrived example to produce side effects that nobody expects. Never do this."

Ternary and comma operator [duplicate]

This question already has answers here:
What's the precedence of comma operator inside conditional operator in C++?
(3 answers)
Closed 6 years ago.
int a,b;
a = 1 ? 1,2 : 3,4; // a = 2
b = 0 ? 1,2 : 3,4; // b = 3
Comma operator returns always the right side of comma, but if we make an assignment to variable it returns left except the case when we use ().
So how the hell the first expression gives the 2.
I see it as a = 1,2 so it should be 1 but actually a=2.
Why?
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.

C++ nested conditional operator order of evaluation

For an expression like
x = a ? b : c ? d : e;
I understand that because the ?: operator has right associativity, the expression is grouped as
x = a ? b : (c ? d : e);
However, what about order of evaluation? Does associativity mean that the (c ? d : e) branch evaluated first, and then the answer of it passed as an argument to the left ?: operator? Or is a evaluated first, and then depending on that either b is returned or the (c ? d : e) branch is evaluated? Or is it undefined?
n3376 5.16/1
Conditional expressions group right-to-left. The first expression is
contextually converted to bool (Clause 4). It is evaluated and if it
is true, the result of the conditional expression is the value of the
second expression, otherwise that of the third expression. Only one of
the second and third expressions is evaluated. Every value computation
and side effect associated with the first expression is sequenced
before every value computation and side effect associated with the
second or third expression.
For the conditional operator:
the first operand is evaluated first;
either the second or the third (but not both) is evaluated depending on the value of the first.

C/C++ How does compiler separate tokens according to operator's precedence and associativity?

Consider the following codes:
int a = 3;
int b = 0;
b = a > 0 ? ++b, ++a : --a, b = 0;
After execution, I get the value of b to become 0 and the value of a to become 4.
That means the result of condition expression, a > 0 is evaluated as true and the expression a++ has been executed, while the expression b = 0 after , hast been executed ,too. In other words, the expression b = 0 is not an operand of the ternary operator, while ++b is. Otherwise, b = 0 won't be executed since the condition expression isn't evaluated as false.
My question is "according to what rule does the compiler kick b = 0 out of the ternary operator's operand?"
The operators in the third statement includes: ++ and --, which have the highest precedence, >, which has the second largest precedence, ? : and =, which have the third largest precedence and , with the lowest precedence. I know that operators with higher precedence should determine their operands earlier so that ++,--, and > are handled first. Then the statement is equivalently:
b = (a > 0) ? (++b), (++a) : (--a), b = 0;
Now, it's = and ?:'s turn to be handled. The associativity of = and ?: is right-to-left, so I consider the compiler will parse the statement from the right end.The first operator met is = and so b = 0 is grouped together. The second met operator is ,. Since it's precedence is lower then the current operators being analyzed, I assume the compiler will just skip it. Then the compiler met :, which is a part of ternary operator, so it keeps parsing.(Actually I don't know how the compiler can know that : is a part of ?: before parsing the whole ternary operator) Problem comes here. The next operator met by the compiler is , but the compiler haven't finished determining the operands of ?: yet. The , has lower priority than ?:. Theoretically it should be skipped; surprisingly, in practical test, the (++b) and (++a) have been concatenated by the , operator at this time and both are considered as the operand of ?:. That makes me confused. Why does the last , is ignored and doesn't included in the operand of ?: while the previous , in statement is kept in the operand of ternary operator?
May someone clarify the concepts of precedence and associativity with this example? I'm really confused about the executing result when first taking a sight of this piece of codes. I had thought that the expression b=0 is also a part of the ternary operator's operand; therefore b = 0 will only be executed if a > 0 is false.
Thanks in advance.
Precedence and associativity are different concepts, but technically the C and C++ standard specifies none. Instead they give the grammar rules to deduce the structure of the expression.
The relevant rules are:
conditional-expression:
logical-or-expression
logical-or-expression ? expression : assignment-expression
expression:
assignment-expression
expression , assignment-expression
primary-expression:
( expression )
postfix-expression:
primary-expression
...
And so on...
The idea is that each type of expression can generate a composite expresion or another type of expression of lower precedence. You can only go up to the root expression by using parenthesis.
With that in mind, note that the conditional-expression that uses the ?: actually has different types of expressions in each of the three subexpressions. The middle one is expression so it will accept any kind of expression, even with , or = (no ambiguity here because of the ending :).
But note that the last one is assignment-expression, that is any kind of expression except the one with ,. If you want to use that, you will have to enclose it with () creating a primary-expression instead.
Bonus explanation: the first expression is logical-or-expression, and if you look carefully to the grammar you'll see that it excludes assignment operators, the conditional operator and the comma operator.
So your expression:
b = a > 0 ? ++b, ++a : --a, b = 0
Is actually a expression comma assignment-expression, where the first expression is b = a > 0 ? ++b, ++a : --a and the second assignment-expression is b = 0.
And so on...
Your expression is evaluated as (b = ((a > 0) ? (++b, ++a) : (--a))), (b = 0);.
As you say the ?: has higher precedence than the comma operator, so the b=0 does not belong to the ternary conditional. The difference for the left and the right part of the ternary operator is, that on the left side the compiler tries to evaluate the complete string ++b, ++a as an expression (knowing that the part between ? and : must be an expression, while on the right side the compiler tries to parse an expression as far as it can. And precedence of operators says the compiler must stop at the ,. On the left side the compiler does not stop on the , because this is a legal part of the expression.

Comma operator precedence while used with ? : operator [duplicate]

This question already has answers here:
Something we found when using comma in condition ternary operator? [duplicate]
(4 answers)
What's the precedence of comma operator inside conditional operator in C++?
(3 answers)
Closed 9 years ago.
I have no idea why the result of the two sub programs below are different:
int a , b;
a = 13, b=12;
(a > b)? (a++,b--):(a--,b++); // Now a is 14 and b is 11
a = 13, b=12;
(a > b)? a++,b-- : a--,b++; // Now a is 14 but b is 12
However for these cases, the results are identical:
a = 13, b=12;
(a < b) ? a++,b-- : a--,b++; // Now a is 12 and b is 13
a = 13, b=12;
(a < b) ? (a++,b--) : (a--,b++); // Again a is 12 and b is 13
Why parentheses make difference for the statement after "?", but make no difference for the statement after ":"? Do you have any idea?
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.
The relevant parts of the C++ grammar are:
conditional-expression:
logical-or-expression
logical-or-expression ? expression : assignment-expression
assignment-expression:
conditional-expression
logical-or-expression assignment-operator assignment-expression
throw-expression
expression:
assignment-expression
expression, assignment-expression
In summary, while the 'middle' of a conditional expression can be a full expression extending up to the :, the last sub-expression can only be an assignment-expression which excludes expressions using the comma operator (other than where the comma operator appears as part of valid sub-expression of an assignment-expression such as a parenthesized primary-expression or as the second operand of another conditional-expression).
In C, the last sub-expression is more restricted, it cannot even be an assignment-expression although this is not a concern in your example.
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
In this case
(a > b)? a++,b-- : a--,b++;
It is equivalent to
((a > b)? a++,b-- : a--),b++;
I guess it's because x ? y cannot be considered a valid expression, therefore the comma can't split the operator there. x ? y : z is a valid expression, the the comma after the colon can split into two expressions.