Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

New Sequence Points in C++11

Tags:

c++

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?

like image 291
Martin York Avatar asked Sep 28 '13 17:09

Martin York


2 Answers

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

like image 163
dyp Avatar answered Oct 05 '22 13:10

dyp


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.

like image 37
Kerrek SB Avatar answered Oct 05 '22 12:10

Kerrek SB