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

2024-04-25

考虑以下代码:

#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>。引入显式强制转换来选择所需的重载。)


简短回答

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指出这一点!)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我可以获取标准库中定义的函数的地址吗? 的相关文章

  • strtok() 使用安全吗[重复]

    这个问题在这里已经有答案了 我读到了很多负面的东西strtok 有人说它已经过时 有人说它不是线程安全的 等等 那么真相是什么 我可以使用吗strtok 它是线程安全的吗 Note 我正在使用 Visual C 您可以使用它 它是标准库的一
  • 信号与信号2

    我的应用程序可能会受益于使用 boost 的信号库之一而不是本土解决方案 该应用程序是多线程的 但执行信号处理的部分是单线程的 如果多线程不是问题 是否有任何理由更喜欢 Boost Signals2 而不是 Boost Signal Boo
  • C# 中类似图的实现

    所以我有一个对象 我们称之为 Head 它有一个对象列表 C C1 C2 C3 T T1 T2 和 M M1 M2 并且所有这些都是相互关联的 例如 Head gt C1 C2 C3 T1 T2 M1 M2 T1 gt C1 C2 T2 g
  • 如何使用c#从数据桶中获取所有文档?

    如何获取数据桶中的所有文档 我尝试过一个示例 但我只能获得一个特定的文档 这是我的代码 CouchbaseClient oclient oclient new CouchbaseClient vwspace data bucket name
  • 为什么像 BindingList 或 ObservableCollection 这样的类不是线程安全的?

    我一次又一次发现自己必须编写 BindingList 和 ObservableCollection 的线程安全版本 因为当绑定到 UI 时 这些控件无法从多个线程更改 我想理解的是why情况就是这样 这是设计错误还是故意的 问题是设计一个线
  • 如何调试.NET Windows Service OnStart方法?

    我用 NET 编写的代码仅在作为 Windows 服务安装时才会失败 该故障甚至不允许服务启动 我不知道如何进入 OnStart 方法 如何 调试 Windows 服务应用程序 http msdn microsoft com en us l
  • 检查两个函数或成员函数指针的签名是否相等

    我编写了一些代码来检查自由函数的签名是否等于成员函数的签名等 它比较提取的返回类型和函数参数 include
  • ASP.net WebForms - 在标记中使用 GetRouteUrl

    我一直在尝试弄清楚如何将路由功能与 ASP net 4 0 WebForms 一起使用 我将一条路线添加到我的路线集合中 void Application Start RegisterRoutes RouteTable Routes voi
  • 使用 OleDbCommand / OleDbDataAdapter 读取 CSV 文件

    我不明白为什么 但是当我使用 OleDbDataAdapter 或 OleDbCommand 读取 CSV 文件时 在这两种情况下 生成的数据结构良好 它识别文件头中的列 但行数据都是空字符串 我之前已经成功进行过多次 CSV 处理 因此我
  • 使用 dateTimePicker 在 DataGridView 中编辑日期

    我有一个DateTime我的 WinForms 中的专栏DataGridView 目前只能通过手动输入日期来编辑该字段 例如 2010 09 02 需要什么才能拥有一个DateTimePicker 或同等 用作编辑器 DataGridVie
  • 使用 cmake 将两种解决方案合二为一

    我有两个单独的 Visual Studio 2013 解决方案 我想将它们迁移到一个解决方案中 因为第一个解决方案 使用 Qt 充当第二个解决方案的 GUI 最后 我希望有一个结构如下的单一解决方案 Solution All Build P
  • 调试错误:在 vc++ 项目中使用 COM 时发生 所需的运行时?

    我为我的工作创建了一个 COM 组件 我也注册了该组件 在我的系统上 我有两个虚拟机工作站 在我的第一个工作站中 它运行良好 在我的第二个工作站中 它显示一个包含消息的错误框该程序需要一段时间并以不寻常的方式关闭 请联系应用程序管理员 我认
  • 意外的 const 引用行为

    include
  • 如果仅使用第一个元素,是否必须为整个结构分配内存?

    我有一个结构 其中第一个元素被测试 并且根据其值 结构的其余部分将被读取或不会被读取 在第一个元素的值指示结构的其余部分不会被读取的情况下 我是否必须为整个结构或仅第一个元素分配足够的内存 struct element int x int
  • 如何重用具有稍微不同的 ProcessStartInfo 实例的 Process 实例?

    我有以下开始的代码robocopy https technet microsoft com en us library cc733145 aspx as a Process 我还需要进行数据库查询以确定每次需要复制哪些目录robocopy被
  • 基础设施 - 同步和异步接口和实现? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 在实现库 基础设施时 并且该 API 的用户希望同步和异步使用代码 我读到混合同步和异步并不是一个好主意 例如 同步实现包括等待异步实现 显然
  • 如何通过代理将套接字连接到http服务器?

    最近 我使用 C 语言编写了一个程序 用于连接到本地运行的 HTTP 服务器 从而向该服务器发出请求 这对我来说效果很好 之后 我尝试使用相同的代码连接到网络上的另一台服务器 例如 www google com 但我无法连接并从网络中的代理
  • C# PasswordDeriveBytes:似乎 Salt 并不重要

    可能我误解了什么 以下代码通过 CryptDeriveKey 使用两种不同的盐生成两个相等的密钥 这是控制台结果 盐1 21 3e 18 a3 9a 8b 5f gt 键 da 89 ea 3d 91 08 20 98 20 e9 dc 4
  • 在 C# 中使用自定义千位分隔符

    在显示字符串时 我尝试不使用 字符作为千位分隔符 而是使用空格 我想我需要定义一种自定义文化 但我似乎做得不对 有什么指点吗 例如 将 1000000 显示为 1 000 000 而不是 1 000 000 no String Replac
  • 创建进程默认浏览器

    我目前正在使用 ShellExecute 打开 在用户浏览器中打开 URL 但在 Win7 和 Vista 中遇到了一些麻烦 因为该程序作为服务运行提升 我想获取线程 id 因此 ShellExecute 无法获取线程 id 因此我开始使用

随机推荐