简短回答
No.
解释
[命名空间.std] http://eel.is/c++draft/namespace.std#6 says:
Let F
表示标准库函数([全局函数] http://eel.is/c++draft/global.functions)、标准库静态成员函数或标准库函数模板的实例化。Unless F
被指定为可寻址函数,如果 C++ 程序显式或隐式尝试形成指向的指针,则该程序的行为是未指定的(可能是格式错误的)F
.
[Note:形成此类指针的可能方法包括应用一元&
操作员 ([expr.unary.op] http://eel.is/c++draft/expr.unary.op), addressof
([专门.地址] http://eel.is/c++draft/specialized.addressof),或函数到指针的标准转换 ([转换功能] http://eel.is/c++draft/conv.func).
— end note]
此外,如果 C++ 程序尝试形成对以下内容的引用,则其行为是未指定的(可能格式错误):F
或者如果它尝试形成一个指向成员的指针,指定标准库非静态成员函数([成员函数] http://eel.is/c++draft/member.functions) 或标准库成员函数模板的实例化。
考虑到这一点,让我们检查这两个调用std::invoke
.
第一次通话
std::invoke(std::boolalpha, std::cout);
在这里,我们试图形成一个指向std::boolalpha
。幸运的是,[fmtflags.manip] http://eel.is/c++draft/fmtflags.manip#1拯救世界:
本节中指定的每个函数都是指定的可寻址函数([命名空间.std] http://eel.is/c++draft/namespace.std).
And boolalpha
是本节中指定的函数。
因此,该行格式良好,相当于:
std::cout.setf(std::ios_base::boolalpha);
但这是为什么呢?那么,下面的代码是必要的:
std::cout << std::boolalpha;
第二次通话
std::cout << std::invoke(static_cast<ctype_func>(std::tolower), 'A') << "\n";
很遗憾,[cctype.syn] http://eel.is/c++draft/cctype.syn#1 says:
标题的内容和含义<cctype>
与C标准库头相同<ctype.h>
.
无处是tolower
显式指定一个可寻址函数。
因此,这个 C++ 程序的行为是未指定的(可能是格式错误的),因为它试图形成一个指向tolower
,未指定为可寻址函数。
结论
无法保证预期输出。
事实上,甚至不能保证代码能够编译。
这也适用于成员函数。
[namespace.std] 没有明确提及这一点,但从 [member.functions] 可以看出,如果 C++ 程序尝试获取声明的成员函数的地址,则其行为是未指定的(可能是格式错误的)在C++标准库中。每[成员.函数]/2 http://eel.is/c++draft/member.functions#2:
对于 C++ 标准库中描述的非虚拟成员函数,实现可以声明一组不同的成员函数签名,前提是对从本文档中描述的声明集中选择重载的成员函数的任何调用行为如下如果选择了该过载。 [Note:例如,实现可以添加具有默认值的参数,或者用具有等效行为的两个或多个成员函数替换具有默认参数的成员函数,或者为成员函数名称添加附加签名。 —end note ]
And [expr.unary.op]/6 http://eel.is/c++draft/expr.unary.op#6:
重载函数的地址只能在唯一确定引用重载函数的版本的上下文中获取(请参阅[over.over])。 [Note:由于上下文可以确定操作数是静态还是非静态成员函数,因此上下文还可以影响表达式的类型是“指向函数的指针”还是“指向成员函数的指针”。 —end note ]
因此,如果程序显式或隐式尝试形成指向 C++ 库中的成员函数的指针,则该程序的行为是未指定的(可能是格式错误的)。
(感谢您的comment https://stackoverflow.com/questions/55687044/can-i-take-the-address-of-a-function-defined-in-standard-library/55687045#comment98781473_55687045指出这一点!)