7

According to cppreference.com, C++20 introduces the "addressing restriction" for standard library functions:

Addressing restriction

The behavior of a C++ program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer, reference (for free functions and static member functions) or pointer-to-member (for non-static member functions) to a standard library function or an instantiation of a standard library function template, unless it is designated an addressable function (see below).

Following code was well-defined in C++17, but leads to unspecified behaviors and possibly fails to compile since C++20:

#include <cmath>
#include <memory>
 
int main()
{
    auto fptr0 = &std::betaf; // by unary operator&
    auto fptr1 = std::addressof(std::betal); // by std::addressof
    auto fptr2 = std::riemann_zetaf; // by function-to-pointer implicit conversion
    auto &fref = std::riemann_zetal; // forming a reference
}

Why was this introduced?

Especially with regard to backwards compatibility of the language, this seems to be a change that breaks a lot of existing code. What benefit does this provide that makes worth such a breaking change?

Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51

1 Answers1

7

It was never "well-defined", it just happened to work on your/some/most implementation. (CppReference is wrong here)

Buried in the introduction of the library section of the standard is a description of what implementors must provide. For (say) std:: riemann_zetal, what's required is that there's a "thing" that can be called using function-call syntax, that takes a single float parameter, and returns something that can be converted to a float.

So, this is a perfectly legal implementation:

template <typename FP>
FP ZETA(FP f, int flags = 0) { /* code here */ } 

#define riemann_zetaf(x)  ZETA<float>(x, 23)

But, you can't take the address of riemann_zetaf because there is no such beast.

Note that even without the macro, you have no guarantee that addressof(riemann_zetal) would return a function that takes exactly one parameter. The only guarantee you have is that you can call it with one parameter.

If you need something that you can take the address of, define it yourself:

float riemann_zetal (float x) { return std::riemann_zetal(x); }

and then take the address of that instead.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Marshall Clow
  • 15,972
  • 2
  • 29
  • 45
  • I doubt the implementation allowed to define such a macro, as it could conflict with user code. – HolyBlackCat Apr 10 '23 at 19:17
  • You mean like `toupper`, say? – Marshall Clow Apr 10 '23 at 19:28
  • 1
    @MarshallClow It would conflict in that a user should be able to declare their own `riemann_zetal` in their scope (it is not a reserved identifier), which doesn't work if it is a macro. (Specifically your suggestion at the end would also fail to compile if the standard library uses the macro implementation.) – user17732522 Apr 10 '23 at 19:38
  • 3
    @MarshallClow `C17 7.1.4 Use of library functions /1` permits C standard library functions to be macros. I can't find the same wording in C++ standard for C++ standard library entities. – HolyBlackCat Apr 10 '23 at 19:41
  • 2
    I think the macro argument is taking the discussion away from the main point, which is: the standard library can have default parameters, templates, or add additional overloads, etc, such that the address is ambiguous, not a matching function, or not available in the first place. There are no guarantees about this ever working, and relying on it is only "probably ok" in reality but not according to the spec. Adhering to the specification protects you against future library changes. – Chris Uzdavinis Apr 10 '23 at 21:24
  • Also, I try to always avoid getting in arguments about the standard library with Marshall Clow. :) – Chris Uzdavinis Apr 10 '23 at 21:25
  • meanwhile updating cppreference to hopefully be less wrong – Cubbi Apr 12 '23 at 02:35
  • @MarshallClow Can you prove that by citing the C++17 standard? I don't want to accept the answer without being sure it is correct. Currently I am not sure. Looks like [P0551](https://wg21.link/p0551) introduced the change to C++20. – Benjamin Buch Apr 17 '23 at 20:52
  • Ok, I was over-broad in my characterization. Implementations are allowed to add extra template arguments to classes, and (I think, but am not sure ATM) to functions (member and non-member), but can only add additional default arguments to member functions. Note that non-member functions can be declared as `inline`, which would make taking their address difficult. – Marshall Clow Apr 17 '23 at 22:41
  • Sorry for the confusion – Marshall Clow Apr 17 '23 at 22:41