16
votes

For example:

int foo(int i) { return i; }

int main()
{
  int i = 0;

  i = i++;      // Undefined
  i = foo(i++); // ?

  return 0;
}

What would the current ISO C++ standard specify for this case?

EDIT:

Here's where I get confused:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

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, and they are not potentially concurrent (1.10), the behavior is undefined.

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

Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.

So it seems you could have a value computation on the left side of the assignment (just i), and a side effect on the right side (the modification of i from i++) which aren't sequenced with respect to each other.

EDIT2:

For anyone who finds themselves here, there is a really great explanation about sequencing that I found here.

2
foo(i++) is fine, foo(i, i++) is undefinedbehzad.nouri
foo(i++) makes a copy hence not increasing its value if you try to print i after the function call. But foo(++i) actually does. Look at the assemblyuser2946316
foo(i++) is fine, foo((i, i++)) is also fine.haccks
This is fine going back to C90. The argument expressions of a function call are all evaluated, then a sequence point takes place before the function is called. The assignment cannot happen until the function returns, because it requires the return value. Where things can go awry is when someone makes foo into a macro.Kaz

2 Answers

15
votes

The last sentence in your quote says "that is not otherwise specifically sequenced before or after the execution of the body of the called function" so the question is whether the increment and the assignment are "otherwise specifically sequenced before or after" the function body.

1.9 [intro.execution] p15 has the answer:

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. [ Note: Value computations and side effects associated with different argument expressions are unsequenced. — end note ]

So the increment of i happens before the function body, and the assignment to i happens after the function returns, so it is perfectly well-defined.

In pre-C++11 terminology, the function call introduces a sequence point between the increment and the assignment.

11
votes

i = foo(i++); is fine, because i++ is executed before foo() is called. A copy of i is made, i is then incremented, then the copy is passed to foo(). It is the same as doing this explicitly:

int tmp = i++;
i = foo(tmp);