两个不同的lambda表达式(没有捕获,相同的args和body)可以衰减到相同的函数指针吗?
我有这个代码:
#include <cassert>
#include <type_traits>
int main() {
auto f0 = [](auto x) { return x; };
auto f1 = [](auto x) { return x; };
static_assert(not std::is_same_v<decltype(f0), decltype(f1)>);
// MSVC Release-mode combines the functions so the pointers are the same (even though the types should be different.
assert(static_cast<int(*)(int)>(f0) != static_cast<int(*)(int)>(f1));
}
https://godbolt.org/z/P3vc45654
我相信static_assert
肯定能通过。assert
能保证通过吗?(我看到MSVC在发布模式下无法在我的计算机上运行assert
。
2条答案
按热度按时间cedebl8k1#
我不同意现有的答案。
您没有使用函数调用运算符。你使用的是“指向函数的指针”的转换。因为lambda表达式有
auto
参数,所以它们是泛型lambda表达式。在这种情况下,向“函数指针”的转换描述如下(N4950,[expr.prim.lambda.closure]/9):
对于没有lambda捕获的泛型lambda,闭包类型有一个转换函数模板指向函数指针。转换函数模板具有与函数调用操作符模板相同的发明模板参数列表,并且函数指针具有相同的参数类型。指向函数的指针的返回类型应表现得好像它是一个decltype-specifier,表示相应函数调用运算符模板专门化的返回类型。
这里没有提到创建指向唯一函数的指针,或者类似的东西。
微软的实现似乎没有违反这里的任何规则。
35g0bw712#
我认为这个问题更多地与Visual Studio构建过程的特性有关,因为Visual Studio编译器中的常量表达式检查正确地证明了它认为两个函数指针是不同的:
在线演示:https://gcc.godbolt.org/z/Msb3zTPjz
请注意,同样的地址问题不仅可以在泛型lambda中观察到,而且可以在普通闭包对象和简单的普通函数中观察到。在最简化的形式中,它可以表示如下:
Visual Studio生成的程序集实际上是正确的,因为编译器真正比较了函数指针是否相等:
在线演示:https://gcc.godbolt.org/z/Mc5qnKzx3
正是 linker 将两个函数合并为一个函数,因为在发布版本中默认启用了
/OPT:ICF
选项。在manual中有如下警告:
因为/OPT:ICF会导致相同的地址被分配给不同的函数或只读数据成员(即使用/戈伊编译时的常量变量),所以它会中断依赖于函数或只读数据成员的唯一地址的程序。有关详细信息,请参见/戈伊(启用函数级链接)。
因此,可以得出结论,这种优化是有用的,但可能会破坏一些有效的C程序。实际上,它不符合C标准[expr.eq/3.2]
比较指针定义如下:...
因为它们不指向同一个函数,所以指针必须比较不相等。