Using the post-increment in function arguments

The C++ standard states (A note in section 1.9.16):

Value computations and side effects associated with the different argument expressions are unsequenced.

In other words, it's undefined and/or compiler-dependent which order the arguments are evaluated in before their value is passed into the function. So on some compilers (which evaluate the left argument first) that code would output 10, 10 and on others (which evaluate the right argument first) it will output 11, 10. In general you should never rely on undefined behaviour.

To help you understand this, imagine that each argument expression is evaluated before the function is called like so (not that this is exactly how it actually works, it's just an easy way to think of it that will help you understand the sequencing):

int arg1 = x;       // This line
int arg2 = x++;     // And this line can be swapped.
print(arg1, arg2);

The C++ Standard says that the two argument expression are unsequenced. So, if we write out the argument expressions on separate lines like this, their order should not be significant, because the standard says they can be evaluated in any order. Some compilers might evaluate them in the order above, others might swap them:

int arg2 = x++;     // And this line can be swapped.
int arg1 = x;       // This line
print(arg1, arg2);

That makes it pretty obvious how arg2 can hold the value 10, while arg1 holds the value 11.

You should always avoid this undefined behaviour in your code.


On a whole the statement:

 print(x, x++);

results in an Undefined Behavior. Once a program has an Undefined Behavior it ceases to be an valid C++ program and literally any behavior is possible.So it is pointless to find reasoning for such an program.


Why is this Undefined Behavior?

Lets evaluate the program step by step to the point where we can beyond any doubt prove that it causes Undefined Behavior.

The order of evaluation of arguments to a function is Unspecified[Ref 1].

Unspecified means that an implementation is allowed to implement this particular functionality as it desires and it is not required to document the detail about it.

Applying the above rule to your function call:

print(x, x++);

An implementation might evaluate this as:

  • Left to Right or
  • Right to Left or
  • Any Magical order(in case of more than two function arguments)

In short you cannot rely on an implementation to follow any specific order because it is not required to as per the C++ Standard.

In C/C++ you cannot read or write to a variable more than once without an intervening sequence point[Ref 2].If you do so it results in an Undefined Behavior.Irrespective of whether either of the arguments gets evaluated first in the said function, there is no sequence point between them,a sequence point exists only after evaluation of all function arguments[Ref 3].

In this case x is being accessed without an intervening sequence point and hence it results in an Undefined Behavior.

Simply put it is best to write any code which does not invoke such Undefined Behaviors because once you do so you cannot expect any specific behavior from such a program.


[Ref 1]C++03 Standard §5.2.2.8
Para 8:

[...] The order of evaluation of function arguments is unspecified. [...]


[Ref 2]C++03 5 Expressions [expr]:
Para 4:

....
Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.


[Ref 3]C++03 1.9 Program execution [intro.execution]:
Para 17:

When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body.


x++ is a function parameter and they may be evaluated in an unspecified order which means the behavior is undefined and not portable (or legal).

Tags:

C++