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
?
MultipleAssignment is a language property of being able to assign one value to more than one variables. It usually looks like the following: a = b = c = d = 0 // assigns all variables to 0.
All types of assignment operators can be mixed in a multiple assignment statement. All varieties of assignment operator have the same precedence, lower than that of any other Java operator. As with simple assignments, evaluation again takes place from right to left.
In highly-object-oriented languages, double assignment results in the same object being assigned to multiple variables, so changes in one variable are reflected in the other.
An assignment operation assigns the value of the right-hand operand to the storage location named by the left-hand operand. Therefore, the left-hand operand of an assignment operation must be a modifiable l-value. After the assignment, an assignment expression has the value of the left operand but is not an l-value.
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 E3
is 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 += 1
is 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 E2
and E3
, the value of the expression E2
refers 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 a
ends 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With