Loki Astari
Loki Astari

Reputation: 264381

New Sequence Points in C++11

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?

Upvotes: 6

Views: 881

Answers (2)

dyp
dyp

Reputation: 39101

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).

Upvotes: 6

Kerrek SB
Kerrek SB

Reputation: 476990

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.

Upvotes: 3

Related Questions