Sorry for opening this topic again, but thinking about this topic itself has started giving me an Undefined Behavior. Want to move into the zone of well-defined behavior.
Given
int i = 0;
int v[10];
i = ++i; //Expr1
i = i++; //Expr2
++ ++i; //Expr3
i = v[i++]; //Expr4
I think of the above expressions (in that order) as
operator=(i, operator++(i)) ; //Expr1 equivalent
operator=(i, operator++(i, 0)) ; //Expr2 equivalent
operator++(operator++(i)) ; //Expr3 equivalent
operator=(i, operator[](operator++(i, 0)); //Expr4 equivalent
Now coming to behaviors here are the important quotes from C++ 0x.
$1.9/12- “Evaluation of an expression
(or a sub-expression) in general
includes both value computations
(including determining the identity of
an object for lvalue evaluation and
fetchinga value previously assigned to
an object for rvalue evaluation) and
initiation of side effects.”$1.9/15- “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.”[ Note: Value computations and side
effects associated with different
argument expressions are unsequenced.
—end note ]$3.9/9- “Arithmetic types (3.9.1),
enumeration types, pointer types,
pointer to member types (3.9.2),
std::nullptr_t, and cv-qualified
versions of these types (3.9.3) are
collectively called scalar types.”
-
In Expr1, the evaluation of the expression
i(first argument), is unsequenced with respect to the evaluation of the expessionoperator++(i)(which has a side effect).Hence Expr1 has undefined behavior.
-
In Expr2, the evaluation of the expression
i(first argument), is unsequenced with respect to the evaluation of the expessionoperator++(i, 0)(which has a side effect)’.Hence Expr2 has undefined behavior.
-
In Expr3, the evaluation of the lone argument
operator++(i)is required to be complete before the outeroperator++is called.Hence Expr3 has well defined behavior.
-
In Expr4, the evaluation of the expression
i(first argument) is unsequenced with respect to the evaluation of theoperator[](operator++(i, 0)(which has a side effect).Hence Expr4 has undefined behavior.
Is this understanding correct?
P.S. The method of analyzing the expressions as in OP is not correct. This is because, as @Potatoswatter, notes – “clause 13.6 does not apply. See the disclaimer in 13.6/1, “These candidate functions participate in the operator overload resolution process as described in 13.3.1.2 and are used for no other purpose.” They are just dummy declarations; no function-call semantics exist with respect to built-in operators.”
Native operator expressions are not equivalent to overloaded operator expressions. There is a sequence point at the binding of values to function arguments, which makes the
operator++()versions well-defined. But that doesn’t exist for the native-type case.In all four cases,
ichanges twice within the full-expression. Since no,,||, or&&appear in the expressions, that’s instant UB.§5/4:
Edit for C++0x (updated)
§1.9/15:
Note however that a value computation and a side effect are two distinct things. If
++iis equivalent toi = i+1, then+is the value computation and=is the side effect. From 1.9/12:So although the value computations are more strongly sequenced in C++0x than C++03,
the side effects are not.Two side effects in the same expression, unless otherwise sequenced, produce UB.Value computations are ordered by their data dependencies anyway and, side effects absent, their order of evaluation is unobservable, so I’m not sure why C++0x goes to the trouble of saying anything, but that just means I need to read more of the papers by Boehm and friends wrote.Edit #3:
Thanks Johannes for coping with my laziness to type “sequenced” into my PDF reader search bar. I was going to bed and getting up on the last two edits anyway… right ;v) .
§5.17/1 defining the assignment operators says
Also §5.3.2/1 on the preincrement operator says
By this identity,
++ ++ xis shorthand for(x +=1) +=1. So, let’s interpret that.1on the far RHS and descend into the parens.1and the value (prvalue) and address (glvalue) ofx.x, which is identical to the glvalue and prvalue result of the subexpression.x +=1.So, then 1 and 3 are well-defined and 2 and 4 are undefined behavior, which you would expect.
The only other surprise I found by searching for “sequenced” in N3126 was 5.3.4/16, where the implementation is allowed to call
operator newbefore evaluating constructor arguments. That’s cool.Edit #4: (Oh, what a tangled web we weave)
Johannes notes again that in
i == ++i;the glvalue (a.k.a. the address) ofiis ambiguously dependent on++i. The glvalue is certainly a value ofi, but I don’t think 1.9/15 is intended to include it for the simple reason that the glvalue of a named object is constant, and cannot actually have dependencies.For an informative strawman, consider
Here, the glvalue of the LHS of
=is dependent on a side-effect on the prvalue ofi. The address ofiis not in question; the outcome of the?:is.Perhaps a good counterexample is
Here
jhas a glvalue distinct from (but identical to)i. Is this well-defined, yeti = ++iis not? This represents a trivial transformation that a compiler could apply to any case.1.9/15 should say