You make the mistake of thinking of operator=
as a two-argument function, where the side effects of the arguments must be completely evaluated before the function begins. If that were the case, then the expression i = ++i + 1
would have multiple sequence points, and ++i
would be fully evaluated before the assignment began. That's not the case, though. What's being evaluated in the intrinsic assignment operator, not a user-defined operator. There's only one sequence point in that expression.
The result of ++i
is evaluated before the assignment (and before the addition operator), but the side effect is not necessarily applied right away. The result of ++i + 1
is always the same as i + 2
, so that's the value that gets assigned to i
as part of the assignment operator. The result of ++i
is always i + 1
, so that's what gets assigned to i
as part of the increment operator. There is no sequence point to control which value should get assigned first.
Since the code is violating the rule that "between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression," the behavior is undefined. Practically, though, it's likely that either i + 1
or i + 2
will be assigned first, then the other value will be assigned, and finally the program will continue running as usual — no nasal demons or exploding toilets, and no i + 3
, either.
It's "undefined behavior," not "unspecified." Undefined means the machine is allowed to do anything including output an empty program, terminate randomly, or explode. Of course, a subtly unexpected value when porting to another platform is a more likely outcome.
Undefined behavior applies to any case where two side effects apply to the same scalar without being sequenced relative to each other. In this case, the side effects happen to be identical (both increment i
from its original value before the expression), but by the letter of the standard, they combine to produce UB.
The side effects are unsequenced because aside from ,
, ?:
, ||
, and &&
, operators do not define sequencing rules in terms such as C++11 §5.15/2:
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 assignment operators do define a special sequencing rule, §5.17/1:
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 does not help i = i ++ + 1
because the side effect of i ++
is not part of any value computation.
Best Answer
The phrase, "…the final value of
i
will be 4 no matter what the order of evaluation…" is incorrect. The compiler could emit the equivalent of this:or this:
or this:
As to the definitions of terms, if the answer was guaranteed to be 4, that wouldn't be unspecified or undefined behavior, it would be defined behavior.
As it stands, it is undefined behaviour according to the standard (Wikipedia), so it's even free to do this: