With the new college year upon us.
We have started to receive the standard why does ++ i ++ not work as expected questions.
After just answering one of these type of questions I was told that the new C++11 standard has changed and this is no longer undefined behavior. I have heard that sequence points have been replaced by sequenced before and sequenced after but have not read deep (or at all) into the subject.
So the question I was just answering had:
int i = 12;
k = ++ (++ i);
So the question is:
How has the sequence points changes in C++11 and how does it affect questions like the above. Is it still undefined behavior or is this now well defined?
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).
I don't think sequencing is relevant to your situation. The expression ++i++ is grouped as ++(i++), so:
If i is a built-in type, then this is invalid, since i++ is an rvalue.
If i is of user-defined type and the operators are overloaded, this is a nested function call, such as T::operator++(T::operator++(i), 0), and function arguments are evaluated before the function call is evaluated.
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