Speaking in the context of the C++11 standard (which no longer has a concept of sequence points, as you know) I want to understand how two simplest examples are defined.
int i = 0;
i = i++; // #0
i = ++i; // #1
There are two topics on SO which explain those examples within the C++11 context. Here it was said that #0
invokes UB and #1
is well-defined. Here it was said that both examples are undefined. This ambiguity confuses me much. I've read this well-structured reference three times already but the topic seems to be way too complicated for me.
.
Let's analyze the example #0
: i = i++;
.
Corresponding quotes are:
The value computation of the built-in postincrement and postdecrement
operators is sequenced before its side-effect.The side effect (modification of the left argument) of the built-in
assignment operator and of all built-in compound assignment operators
is sequenced after the value computation (but not the side effects) of
both left and right arguments, and is sequenced before the value
computation of the assignment expression (that is, before returning
the reference to the modified object)If a side effect on a scalar object is unsequenced relative to another
side effect on the same scalar object, the behavior is undefined.
As I get it, the side effect of the assignment operator is not sequenced with side effects of it's left and right arguments. Thus the side effect of the assignment operator is not sequenced with the side effects of i++
. So #0
invokes an UB.
.
Let's analyze the example #1
: i = ++i;
.
Corresponding quotes are:
The side effect of the built-in preincrement and predecrement
operators is sequenced before its value computation (implicit rule due
to definition as compound assignment)The side effect (modification of the left argument) of the built-in
assignment operator and of all built-in compound assignment operators
is sequenced after the value computation (but not the side effects) of
both left and right arguments, and is sequenced before the value
computation of the assignment expression (that is, before returning
the reference to the modified object)If a side effect on a scalar object is unsequenced relative to another
side effect on the same scalar object, the behavior is undefined.
I can not see, how this example is different from the #0
. This seems to be an UB for me for the very same reason as #0
. The side effect of assignment is not sequenced with the side effect of ++i
. It seems to be an UB. The topic liked above says it is well-defined. Why?
.
Question: how can I apply quoted rules to determine the UB of the examples. An as simple as possible explanation would be greatly appreciated. Thank you!
Best Answer
Since your quotes are not directly from the standard, I will try to give a detailed answer quoting the relevant parts of the standard. The definitions of "side effects" and "evaluation" is found in paragraph 1.9/12:
The next relevant part is paragraph 1.9/15:
Now let's see, how to apply this to the two examples.
This is the postfix form of increment and you find its definition in paragraph 5.2.6. The most relevant sentence reads:
For the assignment expression see paragraph 5.17. The relevant part states:
Using all the information from above, the evaluation of the whole expression is (this order is not guaranteed by the standard!):
i++
(right hand side)i
(left hand side)i
(side effect of++
)i
(side effect of=
)All the standard guarantees is that the value computations of the two operands is sequenced before the value computation of the assignment expression. But the value computation of the right hand side is only "reading the value of
i
" and not modifyingi
, the two modifications (side effects) are not sequenced with respect to each other and we get undefined behavior.What about the second example?
The situation is quite different here. You find the definition of prefix increment in paragraph 5.3.2. The relevant part is:
Substituting that, our expression is equivalent to
Looking up the compound assignment operator
+=
in 5.17/7 we get thati += 1
is equivalent toi = i + 1
except thati
is only evaluated once. Hence, the expression in question finally becomesBut we already know from above that the value computation of the
=
is sequenced after the value computation of the operands and the side effects are sequenced before the value computations of=
. So we get a well-defined order of evaluation:i + 1
(andi
- left hand side of inner expression)(#1)=
, i.e. modify "inner"i
(i = i + 1)
, which is the "new" value ofi
=
, i.e. modify "outer"i
(#1): Here,
i
is only evaluated once, sincei += 1
is equivalent toi = i + 1
except thati
is only evaluated once (5.17/7).