In the expression x * y
, the terms x
and y
are unsequenced. This is one of the three possible sequencing relations, which are:
A
sequenced-before B
: A
must be evaluated, with all side-effects complete, before B
begins evaluationg
A
and B
indeterminately-sequenced: one of the two following cases is true: A
is sequenced-before B
, or B
is sequenced-before A
. It is unspecified which of those two cases holds.
A
and B
unsequenced: There is no sequencing relation defined between A
and B
.
It is important to note that these are pair-wise relations. We cannot say "x
is unsequenced". We can only say that two operations are unsequenced with respect to each other.
Also important is that these relations are transitive; and the latter two relations are symmetric.
unspecified is a technical term which means that the Standard specifies a set number of possible results. This is different to undefined behaviour which means that the Standard does not cover the behaviour at all. See here for further reading.
Moving onto the code x * f(x)
. This is identical to f(x) * x
, because as discussed above, x
and f(x)
are unsequenced, with respect to each other, in both cases.
Now we come to the point where several people seem to be coming unstuck. Evaluating the expression f(x)
is unsequenced with respect to x
. However, it does not follow that any statements inside the function body of f
are also unsequenced with respect to x
. In fact, there are sequencing relations surrounding any function call, and those relations cannot be ignored.
Here is the text from C++14:
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 ]
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.
with footnote:
In other words, function executions do not interleave with each other.
The bolded text clearly states that for the two expressions:
- A:
x = x + 1;
inside f(x)
- B: evaluating the first
x
in the expression x * f(x)
their relationship is: indeterminately sequenced.
The text regarding undefined behaviour and sequencing is:
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 this case, the relation is indeterminately sequenced, not unsequenced. So there is no undefined behaviour.
The result is instead unspecified according to whether x
is sequenced before x = x + 1
or the other way around. So there are only two possible outcomes, 42
and 49
.
In case anyone had qualms about the x
in f(x)
, the following text applies:
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.
So the evaluation of that x
is sequenced before x = x + 1
. This is an example of an evlauation that falls under the case of "specifically sequenced before" in the bolded quote above.
Footnote: the behaviour was exactly the same in C++03, but the terminology was different. In C++03 we say that there is a sequence point upon entry and exit of every function call, therefore the write to x
inside the function is separated from the read of x
outside the function by at least one sequence point.