c++ - 我可以获取标准库中定义的函数的地址吗?

标签 c++ language-lawyer c++-standard-library c++20 unspecified-behavior

考虑以下代码:

#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";
}

在这里,两次调用 std::invoke被标记以供将来引用。 预期的输出是:

a

在 C++20 中是否保证了预期的输出?

(注意:有两个函数称为 tolower — 一个在 <cctype> 中,另一个在 <locale> 中。引入显式强制转换以选择所需的重载。)

最佳答案

简答

没有。

说明

[namespace.std]说:

Let F denote a standard library function ([global.functions]), a standard library static member function, or an instantiation of a standard library function template. Unless F 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 to F. [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 to F 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.

考虑到这一点,让我们检查对 std::invoke 的两次调用.

第一次通话

std::invoke(std::boolalpha, std::cout);

在这里,我们试图形成一个指向 std::boolalpha 的指针.还好,[fmtflags.manip]挽回局面:

Each function specified in this subclause is a designated addressable function ([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]说:

The contents and meaning of the header <cctype> are the same as the C standard library header <ctype.h>.

tolower 无处可寻明确指定一个可寻址函数。

因此,此 C++ 程序的行为是未指定的(可能格式错误),因为它试图形成一个指向 tolower 的指针。 , 未指定可寻址函数。

结论

无法保证预期的输出。 事实上,代码甚至不能保证编译。


这也适用于成员函数。 [namespace.std] 没有明确提及这一点,但从 [member.functions] 可以看出,如果 C++ 程序试图获取声明的成员函数的地址,则其行为是未指定的(可能格式错误)在 C++ 标准库中。每[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 ]

[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 ]

因此,如果程序显式或隐式地尝试形成指向 C++ 库中的成员函数的指针,则该程序的行为是不确定的(可能是格式错误的)。

(感谢 comment 指出这一点!)

关于c++ - 我可以获取标准库中定义的函数的地址吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57438210/

相关文章:

c++ - 转发到 std::ostream << 时操纵器的模板参数推导失败

c++ - 将值分配给以元组为值的嵌套映射时出错

c++ - 是否添加到 "char *"指针 UB,但它实际上并不指向 char 数组?

c++ - typedef 和非简单类型说明符

c++ - 如何可移植地将 std::system_error 异常与 std::errc 值进行比较?

c++ - 我们可以对 std::array 使用传统的指针算法吗?

c++ - 从 C++ 访问函数环境中的 Lua 变量

c++ - PangoLayout中忽略的新行

c++ - 为什么基于 for 循环的范围类型在大括号初始化列表上是非法的 C++

c++ - 使用 setw : to eof or not to eof? 读取