Consider the following code:
#include <cctype>
#include <functional>
#include <iostream>
int main()
{
std::invoke(std::boolalpha, std::cout); // #1
using ctype_func = int(*)(int);
char c = std::invoke(static_cast<ctype_func>(std::tolower), 'A'); // #2
std::cout << c << "\n";
}
Here, the two calls to std::invoke are labeled for future reference.
The expected output is:
a
Is the expected output guaranteed in C++20?
(Note: there are two functions called tolower — one in <cctype> and the other in <locale>. The explicit cast is introduced to select the desired overload.)
Short answer
No.
Explanation
[namespace.std] says:
With this in mind, let's check the two calls to
std::invoke.The first call
Here, we are attempting to form a pointer to
std::boolalpha. Fortunately, [fmtflags.manip] saves the day:And
boolalphais a function specified in this subclause. Thus, this line is well-formed, and is equivalent to:But why is that? Well, it is necessary for the following code:
The second call
Unfortunately, [cctype.syn] says:
Nowhere is
tolowerexplicitly designated an addressable function.Therefore, the behavior of this C++ program is unspecified (possibly ill-formed), because it attempts to form a pointer to
tolower, which is not designated an addressable function.Conclusion
The expected output is not guaranteed. In fact, the code is not even guaranteed to compile.
This also applies to member functions. [namespace.std] doesn’t explicitly mention this, but it can be seen from [member.functions] that the behavior of a C++ program is unspecified (possibly ill-formed) if it attempts to take the address of a member function declared in the C++ standard library. Per [member.functions]/2:
And [expr.unary.op]/6:
Therefore, the behavior of a program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer to a member function in the C++ library.
(Thanks for the comment for pointing this out!)