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 toi
andi++ + 1
, both unsequnced to one another. The second one has side effect oni
, 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 oni
, 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?