从C++20开始保持或传递不可寻址的函数

C++20添加了可寻址函数16.5.4.2.1 [namespace.std]/6:--重点是我的--

让F表示标准库函数([lobal.Functions]),a 标准库静态成员函数,或 标准库函数模板。除非F被指定为 可寻址函数,C++程序的行为未指定 (可能格式错误)如果显式或隐式尝试 指向F.的指针[注意:形成此类指针的可能方法 包括一元&;运算符([expr.unary.op])的应用, AddressOf([Specialized.AddressOf])或函数到指针的标准 转换([conv.func])。-结束语]此外,C++的行为 程序是未指定的(可能格式错误),如果它试图形成 引用F或如果它试图形成指向成员的指针 指定标准库非静态成员函数 或标准库成员的实例化 函数模板。

据我所知,规范未将数学函数标记为可寻址函数。

这是否意味着以下代码从C++20开始是非法的(如noted by cppreference与其他标准库函数的示例一样):

// unspecified and illegal?
auto func = static_cast<float (*)(float, float)>(std::pow);
std::cout << func(2, 4) << std::endl;

下面的代码合法吗?

// legal? or unspecified and illegal?
std::function<float(float, float)> f = static_cast<float(*)(float, float)>(std::pow);
std::cout << f(2, 3) << std::endl;

  • C++20中这一新限制的原因是什么?
  • 这样的限制不是在破坏旧代码吗?
  • 从C++20开始,保持或传递不可寻址函数的正确方式是什么?

解决方案

此规则来自P0551。这里的措辞是"不明确的(可能是错误的)"--不是不明确的行为,不是错误的NDR,不是这样的。

现在,该库在很大程度上是围绕直接使用API进行设计、指定和实现的。库指定x.foo(y, z)的含义,实现必须遵循该规范。但有很多方法可以实现这一点--可能是foo接受一些额外的默认参数,或者可以是模板,或者是重载集。

此外,也许在C++N中,只有x.foo(y, z)。但在C++N+1中,有一个新的建议也添加了x.foo(y)。例如,在C++03中,只有一个vector::push_back,但现在有两个。

C++20中出现此新限制的原因是什么?

限制的原因(它在概念上并不是新的,更多的是因为它最终被表达出来了)是为了允许更改标准库。只有当您获取其中一个函数的地址时,才能观察到这些类型的更改-基本上是库表示它不在乎这些更改是否会破坏您的代码,因为这是您的错误,而不是委员会/库的错误。

另请参阅Standard Library Compatibility。

这样的限制不是在破坏旧代码吗?

不完全是。它对这样做的代码更加不屑一顾,然后不担心未来的任何更改可能会破坏它。

从C++20开始,保持或传递不可寻址函数的正确方式是什么?

用lambda包起来。该lambda甚至可以是无状态的,这允许您仍然将其转换为函数指针。它仍然是一个函数指针,但它不会受到未来标准库更改的影响。

相关文章