简答
没有。
说明
[namespace.std] 说:
让 F 表示标准库函数 ([global.functions])、标准库静态成员函数或标准库函数模板的实例化。
除非 F 被指定为可寻址函数,否则如果 C++ 程序显式或隐式地尝试形成一个指向F的指针。
[注意: 形成此类指针的可能方法包括应用一元 & 运算符 ([expr.unary.op])、addressof ([specialized.addressof]) 或函数到指针的标准转换 ( [conv.func])。
— 尾注 ]
此外,如果 C++ 程序试图形成对 F 的引用,或者如果它试图形成一个指向成员的指针来指定标准库非静态成员函数 ([member.functions]) 或标准库成员函数模板的实例化。
考虑到这一点,让我们检查对std::invoke 的两个调用。
第一次通话
std::invoke(std::boolalpha, std::cout);
在这里,我们试图形成一个指向std::boolalpha 的指针。幸运的是,[fmtflags.manip] 拯救了这一天:
本小节中指定的每个函数都是指定的可寻址函数 ([namespace.std])。
而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] 说:
头文件<cctype>的内容和含义与C标准库头文件<ctype.h>相同。
tolower 没有明确指定可寻址函数。
因此,这个 C++ 程序的行为是未指定的(可能是格式错误的),因为它试图形成一个指向 tolower 的指针,而该指针未被指定为可寻址函数。
结论
无法保证预期的输出。
事实上,代码甚至不能保证编译。
这也适用于成员函数。
[namespace.std] 没有明确提及这一点,但从 [member.functions] 可以看出,如果 C++ 程序试图获取声明的成员函数的地址,则其行为是未指定的(可能是格式错误的)在 C++ 标准库中。每[member.functions]/2:
对于 C++ 标准库中描述的非虚拟成员函数,实现可以声明一组不同的成员函数签名,前提是对成员函数的任何调用都会从本文描述的声明集中选择重载文档的行为就像选择了该重载一样。 [ 注意: 例如,实现可以添加具有默认值的参数,或者将具有默认参数的成员函数替换为具有等效行为的两个或多个成员函数,或者为成员函数名称添加额外的签名。 — 尾注 ]
还有[expr.unary.op]/6:
只能在唯一确定引用哪个版本的重载函数的上下文中获取重载函数的地址(参见 [over.over])。 [ 注意: 由于上下文可能决定操作数是静态成员函数还是非静态成员函数,上下文也会影响表达式的类型是“函数指针”还是“成员函数指针” . — 尾注 ]
因此,如果程序显式或隐式尝试形成指向 C++ 库中成员函数的指针,则程序的行为是未指定的(可能是格式错误的)。
(感谢comment 指出这一点!)