在表达式中调用具有副作用的函数

Calling function with side effects inside expression

我以为我理解序列点在 C++ 中是如何工作的,但是 this GeeksQuiz question 让我感到困惑:

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;
}

这个问题的“正确”答案说它打印了 6561。事实上,在 VS2013 中它打印了。但无论如何它不是 UB,因为无法保证首先评估哪个:f(x, c)x。如果首先评估 f(x, c),我们得到 6561:整个事情变成五个递归调用:前四个 (c = 5, 4, 3, 2) 继续,最后一个 (c = 1) 终止和 returns 1,最后等于9 ** 4

但是,如果先计算 x,那么我们会得到 6 * 7 * 8 * 9 * 1。有趣的是,在 VS2013 中,即使将 f(x, c) * x 替换为 x * f(x, c) 也不会改变结果。并不是说它有任何意义。

按照标准,这个UB是不是?如果不是,为什么?

这是UB。

n4140 §1.9 [intro.execution]/15

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 [...] value computation using the value of the same scalar object [...] the behavior is undefined.

乘法运算符没有明确说明顺序。

这是UB

Order of evaluation of the operands of almost all C++ operators (including the order of evaluation of function arguments in a function-call expression and the order of evaluation of the subexpressions within any expression) is unspecified. The compiler can evaluate operands in any order, and may choose another order when the same expression is evaluated again.

There are exceptions to this rule which are noted below.

Except where noted below, there is no concept of left-to-right or right-to-left evaluation in C++. This is not to 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 of operator+, but the function call to f3 may be evaluated first, last, or between f1() or f2() at run time.