自 C++20 起持有或传递不可寻址函数

Holding or passing around non-addressable-functions since C++20

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

Let F denote a standard library function ([global.functions]), a standard library static member function, or an instantiation of a standard library function template. Unless F is designated an addressable function, the behavior of a C++ program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer to F. [ Note: Possible means of forming such pointers include application of the unary & operator ([expr.unary.op]), addressof ([specialized.addressof]), or a function-to-pointer standard conversion ([conv.func]). — end note ] Moreover, the behavior of a C++ program is unspecified (possibly ill-formed) if it attempts to form a reference to F or if it attempts to form a pointer-to-member designating either a standard library non-static member function ([member.functions]) or an instantiation of a standard library member function template.

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

是否意味着以下代码自 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;

这条规则来自 P0551。这里的措辞是 "unspecified (possibly ill-formed)" - 不是未定义的行为,不是格式错误的 NDR,不是那样的。

现在,该库主要是围绕 API 的直接使用而设计、指定和实现的。该库指定了 x.foo(y, z) 的含义,并且实现必须遵循该规范。但是有很多方法可以实现它——也许 foo 需要一些额外的默认参数,或者可以是一个模板,或者是一个重载集。

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

What is the reason for this new restriction in C++20?

限制的原因(它在概念上并不是新的,更多的是它最终被阐明)是为了允许更改标准库。这些类型的更改只有在您获取其中一个函数的地址时才能观察到——这基本上是图书馆说它不关心这些更改是否会破坏您的代码,因为这是您的错,而不是委员会的错s/library的。

另见 Standard Library Compatibility

Isn't such a restriction breaking legacy code?

不是真的。它对执行此操作的代码更加皱眉,然后不担心将来是否有任何更改可能会破坏它。

What is the right way since C++20 to hold or pass around non-addressable-functions?

将它们包裹在 lambda 中。该 lambda 甚至可以是无状态的,这使您仍然可以将其转换为函数指针。它仍然是一个函数指针,但它不受任何未来标准库更改的影响。