6

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?

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 2
    Possible duplicate of http://stackoverflow.com/questions/3690141/multiple-preincrement-operations-on-a-variable-in-cc/3691469#3691469 – Oberon Sep 28 '13 at 17:51
  • @Oberon: That's not a duplicate. That question explains why it was UB in C++03. I am asking if it is valid in C++11 and if so why the change from "Sequence Points" to "Sequenced Before" changes this behavior. – Martin York Sep 28 '13 at 18:30
  • Exactly that is explained in the linked answer (the one below the accepted answer). – Oberon Sep 28 '13 at 18:32
  • Other related questions/answers: http://stackoverflow.com/a/4183735/420683 http://stackoverflow.com/q/3852768/420683 – dyp Sep 29 '13 at 23:02

2 Answers2

6

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

Community
  • 1
  • 1
dyp
  • 38,334
  • 13
  • 112
  • 177
  • *"The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.* I think that's pretty much all that's needed to say that `++++i` is well defined. – jrok Sep 28 '13 at 18:00
  • @jrok I'm not sure. The result of an increment `++i` is the "updated operand" [expr.pre.incr]/1, so the side-effect of assignment has to be sequenced before the second increment. (And I'm not sure if that falls under the "value computation" category.) – dyp Sep 28 '13 at 18:06
  • So basically it works now because: The ` = ` operator is always sequenced after the evaluation of both parameters. – Martin York Sep 28 '13 at 18:53
  • @LokiAstari Yes, and because such a side-effect is allowed (no UB) if it isn't unsequenced wrt reading from or another side-effect on the same object. – dyp Sep 28 '13 at 18:56
  • `i++` and `++i` can't both be equivalent to `i+=1`. I think you made a typo somewhere. – Oktalist Sep 28 '13 at 21:01
  • @Oktalist Thank you, fixed it. Somehow I didn't notice the second one is a postfix-increment. – dyp Sep 28 '13 at 23:37
3

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.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • What about `++ (++i)` – Martin York Sep 28 '13 at 17:49
  • @LokiAstari: I think that's fine. The inner `++i` has to be evaluated first; there's no ambiguity. It's like `cout << "a" << "b"` -- there's no sequencing problem, even though `cout` gets mutated repeatedly. – Kerrek SB Sep 28 '13 at 17:51
  • @LokiAstari: The usual "problem cases" are things like `i = ++i`, etc. – Kerrek SB Sep 28 '13 at 17:52
  • Second bullet defined in [intro.execution]/15 "When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function." – dyp Sep 28 '13 at 18:00
  • @DyP: Well, yeah, you can definitely get very pedantic about this topic in C++, but as the present question stand, I think it's not terribly deep or mysterious. The linked duplicate question has pretty good answers. – Kerrek SB Sep 28 '13 at 18:03