C++17 中函数指针的求值顺序
Order of evaluation with function pointers in C++17
考虑C++17中的以下程序(及其在注释中的替代方案(:
#include<iostream>
void a(int) {
std::cout << "an";
}
void b(int) {
std::cout << "bn";
}
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
的新指针,所以后来对f
的修改应该对调用没有任何影响。我错了吗?
如果我使用f((f=b,0));
而不是(T(f))((f=b,0));
,两个编译器都会给出相同的输出。在这里,我对未定义的行为方面有点不确定。这是否是未定义的行为,因为f
在评估后仍然引用声明的函数指针,该指针将被参数的评估所修改,如果是这样,为什么会导致未定义的行为而不是调用b
?
我在这里问了一个关于 C++17 中非静态成员函数调用的计算顺序的相关问题。我知道编写这样的代码是危险和不必要的,但我想更好地了解C++标准的细节。
编辑:GCC主干现在也会在Barry提交的错误(见下面的答案(修复后打印a
。不过,Clang和GCC主干仍然显示-Wall
的误报警告。
C++17 规则是,来自 [expr.call]/8:
后缀表达式在表达式列表中的每个表达式和任何默认参数之前排序。参数的初始化,包括每个关联的值计算和副作用,相对于任何其他参数的初始化都是不确定的。
在(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 中函数指针的求值顺序
- 对象指针打印结果以相反的顺序进行
- C++中的结构是否按顺序分配内存?每次都以某种方式获得指针比较的正确答案
- 将字符指针按顺序存储在 map 中 std::map<char*, int> mymap。将其存储为字符或字符串不是一个选项
- 共享指针析构函数中的内存顺序
- 使用指针数组按字母顺序排序
- 带有矢量切片的矢量指针,用于从预序和顺序向量生成树
- SEG故障取决于指针分配的顺序
- "Moving"指向指针的顺序容器
- 关于使用共享指针的评估顺序
- 如何使用 std::shuffle 以随机顺序对具有唯一指针的向量进行洗牌
- C 按顺序排列一个指针阵列
- 为什么指针类型的内置关系运算符不生成C++的总顺序?
- 指针集中元素的顺序
- 允许智能指针按特定顺序删除 SDL 组件
- C++中的指针和引用变量语法顺序
- 如果类实现多个接口,VTable 的方法指针顺序是什么?
- 函数指针和DLL加载顺序
- 新的表达式求值顺序(指针赋值)