Call function with side effects inside an expression

It seemed to me that I understand how sequence points work in C ++, but this GeeksQuiz question puzzled me:

int f(int &x, int c) { c = c - 1; if (c == 0) return 1; x = x + 1; return f(x, c) * x; } int main() { int p = 5; cout << f(p, p) << endl; return 0; } 

The โ€œcorrectโ€ answer to this question says that it prints 6561. Indeed, in VS2013 it does. But is it not UB, because there is no guarantee that is first evaluated: f(x, c) or x . We get 6561 if f(x, c) is first evaluated: it all turns into five recursive calls: the first four (c = 5, 4, 3, 2 ) continue, the last (c = 1) completes and returns 1, which is equal to 9 ** 4 at the end.

However, if x was first evaluated, then we would get 6 * 7 * 8 * 9 * 1 . The funny thing is that in VS2013 even replacing f(x, c) * x with x * f(x, c) will not change the result. Not that it meant anything.

According to the standard, is it UB or not? If not, why?

+6
source share
2 answers

This is UB.

n4140 ยง1.9 [intro.execution] / 15

Except as noted, evaluations of the operands of individual operators and subexpressions of individual expressions are not affected. [...] If the side effect of the scalar object does not depend [...] on calculating the value, using the value the same scalar object [...] behavior is undefined.

Multiplicative operators do not have clearly defined marks.

+4
source

This is UB

The evaluation order of the operands of almost all C ++ operators (including the evaluation order of function arguments in a call function expression and the evaluation order of subexpressions in any expression) is not specified. The compiler can evaluate the operands in any order and can choose a different order when the same expression is evaluated again.

There are exceptions to this rule, which are listed below.

Except as noted below, there is no concept of โ€œleft-rightโ€ or right-to-left in C ++. This should not be confused with left-to-right and right-to-left associativity of operators: the expression f1 () + f2 () + f3 () is parsed as (f1 () + f2 ()) + f3 () due to left-to-right associativity + operator, but the call to f3 can be evaluated first, last, or between f1 () or f2 () at run time.

+2
source

All Articles