C++20 added the rule of addressable function 16.5.4.2.1 [namespace.std]/6: -- emphasis is mine --
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.
The math functions as far as I can tell, are not marked by the spec as addressable functions.
Does it mean that the following code is illegal since C++20 (as noted by cppreference with an example of other standard library functions):
// unspecified and illegal?
auto func = static_cast<float (*)(float, float)>(std::pow);
std::cout << func(2, 4) << std::endl;
and what about the following code, is it legal?
// 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;
This rule comes to us from P0551. The wording here is "unspecified (possibly ill-formed)" - not undefined behavior, not ill-formed NDR, nothing like that.
Now, the library is largely designed, specified, and implemented, around direct use of APIs. The library specifies what x.foo(y, z)
means and the implementation has to follow that specification. But there are many ways that this could be implemented - maybe foo
takes some extra default arguments, or can be a template, or is an overload set.
Moreover, maybe in C++N, there's just x.foo(y, z)
. But then in C++N+1, there's a new proposal that adds x.foo(y)
too. For instance, in C++03 there was just the one vector::push_back
but now there are two.
What is the reason for this new restriction in C++20?
The reason for the restriction (and it isn't really conceptually new, it's more that it's finally articulated) is to permit changing the standard library. These sorts of changes are only observable if you take the address of one of these functions - and it's basically the library saying that it doesn't care if these changes break your code because it's your fault, not the committee's/library's.
See also Standard Library Compatibility.
Isn't such a restriction breaking legacy code?
Not really. It's more heavily frowning upon code that does that and then not being concerned if any future changes might break it.
What is the right way since C++20 to hold or pass around non-addressable-functions?
Wrap them in a lambda. That lambda can even be stateless, which allows you to still convert it to a function pointer. It's still a function pointer, but it's insulated from any future standard library changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With