Skip to content

有趣的问题

来自 andyli 的提问:

cpp
#include <cstdio>
#include <vector>

struct T {
    int x;
};
T& f() {
    puts("f");
    static T x{};
    return x;
}
T& g() {
    puts("g");
    static T x{};
    return x;
}
int main() {
    f() = g();
    f().operator=(g());
}

对于以上代码的运行结果有疑惑:

txt
g
f
g
f

下意识认为应该是先计算 = 左边,理所应当的认为应该先打印 f,然后 g。 然而并非如此,查看求值顺序规则

每个简单赋值表达式 E1 = E2 和每个复合赋值表达式 E1 @= E2 中,E2 的每个值计算和副作用都按顺序早于 E1 的每个值计算和副作用

但是这依旧没办法解释 f().operator=(g()) 的运行结果;以及 f() = g() 这个表达式中使用到的 = 明显不是内建运算符,它是类默认生成的重载运算符

这个可以根据规定:

用运算符写法进行调用时,每个重载的运算符都会遵循它所重载的内建运算符的定序规则。

f() = g(); 可以解释了,但是 f().operator=(g()) 不行,根据规定:

函数调用表达式中,指名函数的表达式按顺序早于每个参数表达式和每个默认实参。

  • 这应当算作 编译器 bug

实测 gcc msvc 都是那个结果,但是 clang 不同。

txt
g
f
f
g

只有 clang 是遵守了规定的,合理的结果。第一个表达式按照了内建赋值运算符的求值规则,第二个表达式按照了函数调用表达式的求值规则。