The UB in those cases is based on [intro.execution]/15
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
For ++(++i)
: [expr.pre.incr]/1 states that ++i
is defined as i+=1
. This leads to [expr.ass]/1, which says
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.
Therefore, for ++(++i)
, equivalent to (i+=1)+=1
, the inner assignment is sequenced before the outer assignment, and we have no UB.
[intro.execution]/15 has an example of UB:
i = i++ + 1; // the behavior is undefined
The case here is a bit different (thanks to Oktalist for pointing out a pre/postfix mistake here). [expr.post.incr]/1 describes the effects of postfix increment. It states:
The value computation of the ++
expression is sequenced before the modification of the operand object.
However, there is no requirement on the sequencing of the side effect (the modification of i
). Such a requirement could also be imposed by the assignment-expression. But the assignment-expression only requires the value computations (but not the side effects) of the operands to be sequenced before the assignment. Therefore, the two modifications via i = ..
and i++
are unsequenced, and we get undefined behaviour.
N.B. i = (i = 1);
does not have the same problem: The inner assignment guarantees the side effect of i = 1
is sequenced before the value computation of the same expression. And the value is required for the outer assignment, which guarantees that it (the value computation of the right operand (i = 1)
) is sequenced before the side effect of the outer assignment. Similarly, i = ++i + 1;
(equivalent to i = (i+=1) + 1;
) has defined behaviour.
The comma operator is an example where the side effects are sequenced; [expr.comma]/1
Every value computation and side effect associated with the left expression is sequenced before every value computation and side effect associated with the right expression.
[intro.execution]/15 includes the example i = 7, i++, i++;
(read: (i=7), i++, i++;
), which is defined behaviour (i
becomes 9
).