Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is `i = i++ + 1` undefined behavior in C++11?

Tags:

c++

c++11

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?

like image 681
j_kubik Avatar asked May 28 '12 02:05

j_kubik


1 Answers

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.

like image 73
Potatoswatter Avatar answered Oct 23 '22 13:10

Potatoswatter