C++17 中函数指针的求值顺序
Order of evaluation with function pointers in C++17
考虑以下 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))
的结果是new指向a
的指针,所以后面修改f
应该对调用没有任何影响.我错了吗?
如果我使用 f((f=b,0));
而不是 (T(f))((f=b,0));
,两个编译器都会给出相同的输出。在这里,我对未定义的行为方面有点不确定。这是否是未定义的行为,因为 f
在评估后仍然引用声明的函数指针,这将通过参数的评估被修改,如果是这样,为什么 会导致未定义的行为而不是调用 b
?
我问了一个关于 C++17 中非静态成员函数调用求值顺序的相关问题 。我知道这样写代码是危险和不必要的,但我想更好地理解 C++ 标准的细节。
编辑:在修复了 Barry 提交的错误(参见下面他的回答)后,GCC 主干现在也打印 a
。不过,Clang 和 GCC 主干仍然显示 -Wall
的误报警告。
C++17 规则是,来自 [expr.call]/8:
The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.
在(T(f))((f=b,0));
中,(T(f))
在(f=b, 0)
的参数初始化之前排序。所有这些都是明确定义的,程序 应该 打印 "a"。也就是说,它的行为应该像:
auto __tmp = T(f);
__tmp((f=b, 0));
即使我们更改您的程序以使其有效,情况也是如此:
T{f}(f=b, 0); // two parameters now, instead of one
f=b
和 0
表达式之间的顺序不确定,但是 T{f}
仍然排在两者之前,所以这仍然会调用 a
.
归档91974.
考虑以下 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))
的结果是new指向a
的指针,所以后面修改f
应该对调用没有任何影响.我错了吗?
如果我使用 f((f=b,0));
而不是 (T(f))((f=b,0));
,两个编译器都会给出相同的输出。在这里,我对未定义的行为方面有点不确定。这是否是未定义的行为,因为 f
在评估后仍然引用声明的函数指针,这将通过参数的评估被修改,如果是这样,为什么 会导致未定义的行为而不是调用 b
?
我问了一个关于 C++17 中非静态成员函数调用求值顺序的相关问题
编辑:在修复了 Barry 提交的错误(参见下面他的回答)后,GCC 主干现在也打印 a
。不过,Clang 和 GCC 主干仍然显示 -Wall
的误报警告。
C++17 规则是,来自 [expr.call]/8:
The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.
在(T(f))((f=b,0));
中,(T(f))
在(f=b, 0)
的参数初始化之前排序。所有这些都是明确定义的,程序 应该 打印 "a"。也就是说,它的行为应该像:
auto __tmp = T(f);
__tmp((f=b, 0));
即使我们更改您的程序以使其有效,情况也是如此:
T{f}(f=b, 0); // two parameters now, instead of one
f=b
和 0
表达式之间的顺序不确定,但是 T{f}
仍然排在两者之前,所以这仍然会调用 a
.
归档91974.