Can I take the address of a function defined in standard library?
Short answer
No.
Explanation
[namespace.std] says:
Let
F
denote a standard library function ([global.functions]), a standard library static member function, or an instantiation of a standard library function template.
UnlessF
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 toF
.
[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 toF
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.
With this in mind, let's check the two calls to std::invoke
.
The first call
std::invoke(std::boolalpha, std::cout);
Here, we are attempting to form a pointer to std::boolalpha
. Fortunately, [fmtflags.manip] saves the day:
Each function specified in this subclause is a designated addressable function ([namespace.std]).
And boolalpha
is a function specified in this subclause.
Thus, this line is well-formed, and is equivalent to:
std::cout.setf(std::ios_base::boolalpha);
But why is that? Well, it is necessary for the following code:
std::cout << std::boolalpha;
The second call
std::cout << std::invoke(static_cast<ctype_func>(std::tolower), 'A') << "\n";
Unfortunately, [cctype.syn] says:
The contents and meaning of the header
<cctype>
are the same as the C standard library header<ctype.h>
.
Nowhere is tolower
explicitly 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:
For a non-virtual member function described in the C++ standard library, an implementation may declare a different set of member function signatures, provided that any call to the member function that would select an overload from the set of declarations described in this document behaves as if that overload were selected. [ Note: For instance, an implementation may add parameters with default values, or replace a member function with default arguments with two or more member functions with equivalent behavior, or add additional signatures for a member function name. — end note ]
And [expr.unary.op]/6:
The address of an overloaded function can be taken only in a context that uniquely determines which version of the overloaded function is referred to (see [over.over]). [ Note: Since the context might determine whether the operand is a static or non-static member function, the context can also affect whether the expression has type “pointer to function” or “pointer to member function”. — end note ]
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!)
Alternative to taking the address of a standard library function / possibly ill-formed behaviour
is there no safe way to call (in this example) toupper by pointer?
Not directly, only through one level of indirection (see below).
does a
static_cast
make the function pointer to a std lib function safe?
No. It can nail down an overload set to one particular function signature, but this doesn't have anything to do with whether you're allowed to take the address of that function.
Is there another way to achieve the below functionality via a single function with signature
int fun(int)
There is an alternative, you can wrap the function call in two lambdas. This requires little change to the original snippet:
bool choice = true;
int (*fun)(int);
if (choice)
fun = [](int ch){ return std::toupper(ch); };
else
fun = [](int ch){ return std::tolower(ch); };
int t = fun('x');
This works nicely because both lambdas have no state and the same signature, so they implicitly convert to the function pointer.
Holding or passing around non-addressable-functions since C++20
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.
Why I can't take address of the std::addressof function
As you already hinted yourself, taking the address of std::addressof
has unspecified behavior, and so may or may not work, because it is not designated as an addressable function in the standard.
More practically speaking though, the issue is that there are at least two overloads for std::addressof
, both being templates with one template parameter.
One taking T&
as a function parameter and the other taking const T&&
as a function parameter (where T
is the template parameter). The latter is defined as delete
'd.
std::addressof<int>
is not sufficient to decide which of these to choose.
The library implementation could also choose to implement these specified overloads in different ways, or add additional overloads, which is why taking the address is unspecified.
Where are the functions in the C standard library defined?
gcc
comes with (binary) object files (not C source files) which contain implementations of all the standard C functions. When you use gcc
to link object files into an executable file, the linker automatically includes the object files which implement the standard library functions. According to this thread, that standard object file will probably be called libc.a
or libc.so
.
Say you include a call to printf
in your program. When the linker tries to resolve where that call should go, it will find the definition of printf
in libc.a
, and make your function call point there.
Look at http://gcc.gnu.org/onlinedocs/gcc/Link-Options.html and note the -nostdlib
and -nodefaultlibs
options. You can use these options to tell gcc
's linker not to include the standard library object files by default.
How to pass a function template as a template argument?
You will have the same issue with other io manipulators that typically are functions that take the stream as parameter, when they are templates. Though you can wrap them in a non-template callable:
#include <iostream>
template<typename... Args>
void print(Args const&... args)
{
(std::cout << ... << args);
}
int main()
{
std::cout << 1 << 2 << 3 << std::endl; // ok
print(1, 2, 3); // ok
print(1, 2, 3, [](std::ostream& o) -> std::ostream&{
o << std::endl;
return o;
}); // no error!
}
Output:
123
123123
The syntax is rather heavy so you might want to use a helper type, though I'll leave it to you to write that (just joking, I don't think it is trivial, but I might give it a try later ;). After pondering about it for a while, I am almost certain that there are only the two alternatives: Instantiate the function (see other answer), or wrap the call inside a lambda, unless you want to write a wrapper for each single io manipulator of course.
Related Topics
Why Can't I Use Float Value as a Template Parameter
Remove Comments from C/C++ Code
Forward Declaration of a Typedef in C++
Why Doesn't Std::Queue::Pop Return Value.
Why No Default Move-Assignment/Move-Constructor
Is Stateful Metaprogramming Ill-Formed (Yet)
Read Numeric Data from a Text File in C++
Registering a Cpp Dll into Com After Installation Using Wix Msi Installer
Use 'Class' or 'Typename' For Template Parameters
Flags to Enable Thorough and Verbose G++ Warnings
What's the Difference Between a Header File and a Library
How to Build Qt For Visual Studio 2010
How to Use Stringstream to Separate Comma Separated Strings
Executing Cv::Warpperspective For a Fake Deskewing on a Set of Cv::Point