20

I am reading n3290 draft of C++11 standard (as close as I could get to actual standard text), and I noticed that i = i++ + 1; produces undefined behavior. I have seen similar questions before, but they were answered in terms of older standards (Sequence points). New standard introduces instead concept of Sequencing before/after relation between expression and sub-expression executions.

1.9 13 Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations executed by a single thread (1.10), which induces a partial order among those evaluations. Given any two evaluations A and B, if A is sequenced before B, then the execution of A shall precede the execution of B. If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced. [ Note: The execution of unsequenced evaluations can overlap. —end note ] Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which. [ Note: Indeterminately sequenced evaluations cannot overlap, but either could be executed first. —end note ]

1.9 14 Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

1.9 15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. —end note ] 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 anotherside effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

[ Example:
void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
}
—end example ]

The way I understand it, it works like that:

  • operator= has two operand expressions: taking reference to i and i++ + 1, both unsequnced to one another. The second one has side effect on i, but the first one seems to me not to have side effect or be used in value computation (or is reference taking "a value computation using the value of the same scalar object"? does it actually depend on value stored in i? don't think so), so it's not undefined behavior;
  • operator= execution is sequenced after both operand evaluation. It has side effect on i, but it's well-sequenced in reference to both operands so it's not udefined behavior;
  • i++ + 1 is obviously defined behavior.

Am I wrong about something here? Or is this line undefined behaviour for some other reason?

PS. Standard actually says

The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.

, and it doesn't mention side effects in this context at all. However sequencing relation is defined only between expression evaluations, and evaluation = value computing + side effects. So either I have to assume this draft to be inconsistent here, or assume that in this line they meant evaluation instead of value computation. Or am I wrong here?

EDIT:

I guess I will be answering myself here, but that was the reason for my confusion:

5 1 An expression is a sequence of operators and operands that specifies a computation. An expression can result in a value and can cause side effects.

So operands of operators are not sub-expressions themselves. Therefore only value computation for entire i = i++ + 1; is sequenced, and no mention of side effect sequencing is made by standard. That's the reason why it's undefined.

Note that if eg. operator= was overloaded for given type (so it would be an implicite function call) it wouldn't be undefined behavior, right?

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
j_kubik
  • 6,062
  • 1
  • 23
  • 42
  • 2
    `i++ + 1` or `++ i + 1`? The title doesn't match the question. – Potatoswatter May 28 '12 at 02:06
  • Doesn't the draft mention 'sequence points'? – Arafangion May 28 '12 at 02:08
  • @Arafangion The standard was ratified over a year ago so the "draft" doesn't matter, and no, they *removed* any mention of sequence points which were the old-fashioned specification from C days. – Potatoswatter May 28 '12 at 02:11
  • 2
    No it's not a duplicate, i specifically wanted to avoid confusing those two questions. – j_kubik May 28 '12 at 02:20
  • Ah, sorry, I did wait a few minutes before making that nomination. Your self answer is incorrect. All the value computations are well sequenced but the side effects of `=` and `++` are not. This produces UB even though they have the same effect. From the answer I began to write, "UB means anything can happen, including an empty executable program, random termination, or explosion." The compiler doesn't merely choose between two ways of incrementing `i`. Also, overloading `=` wouldn't make it well-defined because that makes the LHS and RHS arguments to a function call, which aren't sequenced. – Potatoswatter May 28 '12 at 02:29
  • It's not merely unspecified, it's undefined. If it were merely unspecified, the standard would specify two or more possible behaviors from which an implementation may choose arbitrarily. For undefined behavior, the standard imposes no requirements at all. – Keith Thompson May 28 '12 at 02:32

3 Answers3

7

It's "undefined behavior," not "unspecified." Undefined means the machine is allowed to do anything including output an empty program, terminate randomly, or explode. Of course, a subtly unexpected value when porting to another platform is a more likely outcome.

Undefined behavior applies to any case where two side effects apply to the same scalar without being sequenced relative to each other. In this case, the side effects happen to be identical (both increment i from its original value before the expression), but by the letter of the standard, they combine to produce UB.

The side effects are unsequenced because aside from ,, ?:, ||, and &&, operators do not define sequencing rules in terms such as C++11 §5.15/2:

If the second expression is evaluated, every value computation and side effect associated with the first expression is sequenced before every value computation and side effect associated with the second expression.

The assignment operators do define a special sequencing rule, §5.17/1:

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.

This does not help i = i ++ + 1 because the side effect of i ++ is not part of any value computation.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
4

in C++03 both i = ++i + 1; and i = i++ + 1 are not well defined.

But in C++11 the i = ++i + 1 becomes well defined. but i=i++ + 1 is still not.

Look the content in this like for details Sequencing rules and example disagree

RolandXu
  • 3,566
  • 2
  • 17
  • 23
3

You are confusing value computation with resolution of side effects. While the value of i++ must be computed before the assignment, nothing sequences the side-effect (the modification to i) with respect to the assignment other than the completion of the full expression.

For a contrasting example, have a look at the comma operator: "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." Notice how value computation and side effects are mentioned separately. There is no such rule for assignment.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • What about `i = 7, i++, i++; // i becomes 9`? – Potatoswatter May 28 '12 at 02:07
  • It depends on whether these are three full expressions or one. I don't recall offhand how the comma operator is defined. If those are three full expressions, then it's defined. If it's one, then it's UB. – David Schwartz May 28 '12 at 02:08
  • 1.9/10: "A full-expression is an expression that is not a subexpression of another expression." No, it's not UB, I just copied the example from the OP (who copied from the standard). – Potatoswatter May 28 '12 at 02:09
  • The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. - doesn;t that apply to assignment operator as well? Also read my PS after question... – j_kubik May 28 '12 at 02:09
  • @j_kubik: It applies to the assignment operator as well. But side-effects are not value computations. The side-effect is only sequenced to the statement. – David Schwartz May 28 '12 at 02:10
  • 2
    @Potatoswatter: The comma operator: *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.* – dirkgently May 28 '12 at 02:10
  • 2
    Thanks, dirkgently. Notice how the comma operator specifically mentions the sequencing of the side effects in addition to the value computations. That's what makes that case well-defined. – David Schwartz May 28 '12 at 02:14
  • @dirkgently Yes, but tell that to @ David, not me! – Potatoswatter May 28 '12 at 02:14
  • @Potatoswatter: Ah! My bad! I thought you had a serious question there. I'm usually down on my irony-fu at around this time. – dirkgently May 28 '12 at 02:16
  • @dirkgently It's OK. Happy towel day! – Potatoswatter May 28 '12 at 02:17
  • @DavidSchwartz yes you are right - I added a bit longer explanation to my original question – j_kubik May 28 '12 at 02:24
  • @DavidSchwartz If your opinion has changed since you wrote the answer, you should edit it. I might even remove the downvote. – Potatoswatter May 28 '12 at 03:56
  • @Potatoswatter: I believe my answer is correct. Nothing specifies that the side-effect is sequenced. The entire statement is a single expression. Honestly, I have no idea why you downvoted. – David Schwartz May 28 '12 at 06:19
  • @DavidSchwartz "Notice how the comma operator specifically mentions the sequencing of the side effects in addition to the value computations. That's what makes that case well-defined." Edit: Ah, now I see. Your answer is correct in the context of the expression `i = i++ + 1`, it's not intended to cover the other examples at all. – Potatoswatter May 28 '12 at 11:49
  • @Potatoswatter: Did you miss that his question was about `i = i++ + 1;`? And my answer was that nothing specifies that the side-effect is sequenced. The comma case provides a good contrast and makes it clear that my answer is correct. (There, something does specify that the side-effect is sequenced. Sequencing of value computation does not imply sequencing of side-effects. That's what the OP missed.) – David Schwartz May 28 '12 at 11:51
  • Sorry, I if I could accept two answers, I would accept yours as well. – j_kubik May 29 '12 at 00:12