If you have the following:
if (x)
{
y = *x;
}
else
{
y = 0;
}
Then behavior is guaranteed to be defined since we can only dereference x if it is not 0
Can the same be said for:
y = (x) ? *x : 0;
This seems to work as expected (even compiled with -Wpedantic on g++)
Is this guaranteed?
Yes, only the second or third operand will be evaluated, the draft C++ standard section 5.16 [expr.cond] says:
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.
Related
While porting some C code to Windows, I've discovered an interesting ternary operator behavior in MSVC++. It appears that compiler evaluates both branches around ? : in the following example:
#include <stdio.h>
struct S {
int x;
};
int getNum() {
printf("get num\n");
return 4;
}
int main(int argc, char **argv) {
struct S s = argc ? (struct S) { .x = getNum() } : (struct S) { .x = getNum() };
printf("%d\n", s.x);
return 0;
}
Prints:
get num
get num
4
But, GCC and Clang evaluate getNum() only once. Which behavior is correct or allowed by the standard?
According to C++11 §5.16.1 Conditional operator:
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.
According to C11 §6.5.15 Conditional operator:
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.
Many times I see (and sometimes write) code similar to this example:
int a=0, b=2;
if( a && (b=func())!=0 ) {
//...
The question is: does the standard guarantee these statements?
b will be not touched (and remain value 2)
func() will not be called
And vice-versa, if we write if( func()!=0 && a ) - does the standard guarantee func() will be called?
I'm interested in the particular standard paragraph defining this is legitimate.
UPD: my typo, changed from int a=1 to int a=0
To the exact question;
The question is: does standard guarantee these statements?
To the updated question; given a=0. If a==0, then yes, the short circuit evaluation would kick in and func() would not be called; the second operand would not be evaluated.
If a=1 (as it was originally), the opposite; func() will be called - a is 1 thus "true", as a result the second operand is evaluated (it is a logical AND), b will change. If the operator had been || (logical OR), then short circuit evaluation would kick in and func() would not be called.
And vice-versa, if we write if( func()!=0 && a ) -- does standard guarantee func() will be called?
Yes, the first operand is always evaluated.
Yes, short circuit evaluation is guaranteed for C++;
§5.14 Logical AND operator
1 The && operator groups left-to-right. The operands are both contextually converted to bool (Clause 4). The result is true if both operands are true and false otherwise. Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false.
2 The result is a bool. If the second expression 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 expression.
§5.15 Logical OR operator
1 The || operator groups left-to-right. The operands are both contextually converted to bool (Clause 4). It returns true if either of its operands is true, and false otherwise. Unlike |, || guarantees left-to-right evaluation; moreover, the second operand is not evaluated if the first operand evaluates to true.
2 The result is a bool. If the second expression 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 expression.
The corresponding quotes for C are;
§6.5.13 Logical AND operator
4 Unlike the bitwise binary & operator, the && operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares equal to 0, the second operand is not evaluated.
§6.5.14 Logical OR operator
4 Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares unequal to 0, the second operand is not evaluated.
From the C-90 standard.
6.5.13 Logical AND operator
....
4 Unlike the bitwise binary & operator, the && operator guarantees left-to-right evaluation;
there is a sequence point after the evaluation of the first operand. If the first operand
compares equal to 0, the second operand is not evaluated.
Similarly for the Logical OR operator.
The && operator requires both operands to be true. If the first operand evaluates to false, then the second operand will not be evaluated. But beause a is 1, it is considered true and the second expression (operand) is evaluated. Thus func() is called and its result assigned to b and then b is tested to be non-zero.
The standard guarantees that the statements in a sequence of && are evaluated from left to right, and that as soon as one of them evaluates to false, the ones to the right of that will not be evaluated.
The question is: does standard guarantee these statements?
b will be not touched (and remain value 2)
func() will not be called
No, in fact both of them wrong in this case. Because it's operator && so no shortcut logic can be applied in this particular case.
If you change it to || then your statements are correct - only then the evaluation of the first operand (a = 1 in this case) will be enough and the rest is ignored.
As the question changed to a = 0 then yes, both statements are correct and guaranteed.
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.
The C++11 standard (5.17, expr.ass) states that
In all cases, the assignment is sequenced after the value computation
of the right and left operands, and before the value computation of
the assignment expression. With respect to an
indeterminately-sequenced function call, the operation of a compound
assignment is a single evaluation
As I understand it, all expressions which are a part of the given assignment will be evaluated before the assignment itself. This rule should work even if I modify the same variable twice in the same assignment, which, I am fairly certain, was undefined behavior before.
Will the given code:
int a = 0;
a = (a+=1) = 10;
if ( a == 10 ) {
printf("this is defined");
} else {
printf("undefined");
}
always evaluate to a==10?
Yes, there was a change between C++98 and C++11. I believe your example to be well-defined under C++11 rules, while exhibiting undefined behavior under C++98 rules.
As a simpler example, x = ++x; is undefined in C++98 but is well-defined in C++11. Note that x = x++; is still undefined (side effect of post-increment is unsequenced with the evaluation of the expression, while side effect of pre-increment is sequenced before the same).
Let's rewrite your code as
E1 = (E2 = E3)
where E1 is the expression a, E2 is the expression a += 1 and E3 is the expression 10. Here we ussed, that the assignment operator groups right-to-left (§5.17/1 in C++11 Standard).
§5.17/1 moreover states:
In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.
Applying this to our expression means that we first must evaluate the subexpressions E1 and E2 = E3. Note that there is no "sequenced-before" relationship between these two evaluations, but that causes no problems.
The evaluation of the id-expression E1 is trivial (the result is a itself). The evaluation of the assignment-expression E2 = E3 proceeds as follows:
First both subexpressions have to be evaluated. The evaluation of the literal E3is again trivial (gives a prvalue of value 10).
The evaluation of the (compound) assignment-expression E2 is done in the following steps:
1) The behavior of a += 1is equivalent to a = a + 1 but a is only evaluated once (§5.17/7). After evaluating the subexpressions a and 1 (in an arbitrary order), an lvalue-to-rvalue conversion is applied to a in order to read the value stored in a.
2) The values of a (which is 0) and of 1 are added (a + 1) and the result of this addition is a prvalue of value 1.
3) Before we can compute the result of the assignment a = a + 1 the value of the object the left operand refers to is replaced by the value of the right operand (§5.17/2). The result of E2 is then an lvalue refereing to the new value 1. Note that the side effect (updating the value of the left operand) is sequenced before the value computation of the assignment expression. This is §5.17/1 cited above.
Now that we have evaluated the subexpressions E2and E3, the value of the expression E2refers to is replaced by the value of E3, which is 10. Hence the result of E2 = E3 is an lvalue of value 10.
Finally, the value expression E1 refers to is replaced by the value of the expression E2 = E3, which we computed to be 10. Thus, the variable aends up to contain the value 10.
Since all these steps are well-defined, the whole expression yields a well-defined value.
After doing a little research, I am convinced your codes behaviour is well defined in C++11.
$1.9/15 states:
The value computations of the operands of an operator are sequenced before
the value computation of the result of the operator.
$5.17/1 states:
The assignment operator (=) and the compound assignment operators all group
right-to-left.
If I understand correctly, in your example
a = (a+=1) = 10;
this implies that the value computations of (a+=1) and 10 have to be made before the value computation of (a+=1) = 10 and the value computation of this expression has to be finished before a = (a+=1) = 10; is evaluated.
$5.17/1 states:
In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.
This implies that the assignment must happen before the value computation, and therefore, due to transitivity, the evaluation of (a+=1) = 10 can only begin after the assignment a+=1 (Because its value may only be computed after the side effect).
The same is true for the second and third assignment.
See also this excellent answer, which explains the sequenced-before relation in much more detail and way better than I could.
Lets say i have following code:
std::vector<T> R;
if (condition) R = generate();
...
for (int i = 0; i < N; ++i) {
const auto &r = (R.empty() ? generate() : R);
}
It appears that generate is called regardless of R.empty(). Is that standard behavior?
From Paragraph 5.16/1 of the C++ 11 Standard:
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.