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