【发布时间】:2020-01-31 21:31:57
【问题描述】:
考虑以下 C++17 中的程序(以及注释中的替代程序):
#include<iostream>
void a(int) {
std::cout << "a\n";
}
void b(int) {
std::cout << "b\n";
}
int main() {
using T = void(*)(int);
T f = a;
(T(f))((f=b,0)); // alternatively: f((f=b,0))
}
使用 -O2 选项,Clang 9.0.0 打印 a 和 GCC 9.2 打印 b。两者都警告我未排序的修改和访问f。见godbolt.org。
我的期望是这个程序具有明确定义的行为并将打印a,因为 C++17 保证调用 (T(f)) 的左侧表达式在之前排序对论点的任何评估。因为表达式(T(f)) 的结果是一个指向a 的new 指针,所以后面对f 的修改应该对调用完全没有影响。我错了吗?
如果我使用f((f=b,0)); 而不是(T(f))((f=b,0));,两个编译器都会给出相同的输出。在这里,我对未定义的行为方面有点不确定。这会是未定义的行为吗,因为f 在评估后仍然引用已声明的函数指针,该函数指针将通过对参数的评估进行修改,如果是这样,为什么会导致未定义的行为而不是打电话给b?
关于 C++17 here 中非静态成员函数调用的求值顺序,我提出了一个相关问题。我知道编写这样的代码是危险且不必要的,但我想更好地了解 C++ 标准的细节。
编辑:在 Barry 提交的错误(请参阅下面的答案)得到修复后,GCC 主干现在也打印 a。不过,Clang 和 GCC 主干仍然会显示带有 -Wall 的误报警告。
【问题讨论】:
-
人们并没有非常彻底地检查编译器是否真的实现了所有新的评估规则顺序。如果您继续试验,请使用最新的编译器开发版本,并准备提交错误。
标签: c++ language-lawyer c++17 function-pointers order-of-execution