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, a
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.
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.
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).