C++ 获取指定的重载函数地址
刚刚看到一篇博客,说 std::bind 无法绑定正确的重载函数。这里的问题并不是 std::bind 能力不足,而是将函数名传递给 std::bind 时编译器无法取到这个函数的地址(也就是符号,编译器会先解析成符号,链接器再替换为地址),因为有多个重载函数都是这个名字。核心问题是无法通过函数名取到想要的重载函数地址。就像下面的代码无法编译通过:
#include <iostream>
void f()
{
std::cout << "f 1" << std::endl;
}
void f(int x)
{
std::cout << "f 2 " << x << std::endl;
}
int main()
{
auto p = &f;
}
编译错误:
/home/abc/cpplearn/overload_func.cpp: In function ‘int main()’:
/home/abc/cpplearn/overload_func.cpp:15:15: error: unable to deduce ‘auto’ from ‘& f’
15 | auto p = &f;
| ^
/home/abc/cpplearn/overload_func.cpp:15:15: note: couldn’t deduce template parameter ‘auto’
有没有什么比较完美的解决办法呢?我觉得一定有,因为 C 语言没有函数重载,函数地址作为实参也是常规操作。相比之下,C++ 引入了函数重载,却无法取到函数地址,这就很尴尬。C++ 设计者肯定也想到了这个问题。
于是查阅了 cppreference.com,看到了 Address of an overloaded function。函数名的重载解析除了发生在函数调用的时候,也会发生在以下 7 种语境:
| # | Context | Target |
|---|---|---|
| 1 | initializer in a declaration of an object or reference | the object or reference being initialized |
| 2 | on the right-hand-side of an assignment expression | the left-hand side of the assignment |
| 3 | as a function call argument | the function parameter |
| 4 | as a user-defined operator argument | the operator parameter |
| 5 | the return statement |
the return type of a function |
| 6 | explicit cast or static_cast argument |
the target type of a cast |
| 7 | non-type template argument | the type of the template parameter |
当函数名存在于这 7 种语境时,会发生重载解析,并且会选择与 Target 类型匹配的那个重载函数。这里就不一一考察这 7 种语境了,有兴趣可以自己查阅 cppreference.com。这里重点考察第 3 种和第 6 种。
先看第 3 种语境。当函数名作为函数调用的实参时,重载解析会选择和形参类型相匹配的版本。也就是说,下面的代码会如期运行:
#include <iostream>
void f()
{
std::cout << "f 1" << std::endl;
}
void f(int x)
{
std::cout << "f 2 " << x << std::endl;
}
void call(void p(int)) {
p(1);
}
int main()
{
call(f);
}
这段代码输出:
f 2 1
回到最初的问题,std::bind 也是函数,为什么无法正常编译呢?直接分析一下面代码的编译错误信息:
#include <iostream>
#include <functional>
void f()
{
std::cout << "f 1" << std::endl;
}
void f(int x)
{
std::cout << "f 2 " << x << std::endl;
}
int main()
{
auto new_func = std::bind(f, std::placeholders::_1);
new_func(66);
}
编译错误:
/home/abc/cpplearn/overload_func.cpp: In function ‘int main()’:
/home/abc/cpplearn/overload_func.cpp:16:30: error: no matching function for call to ‘bind(<unresolved overloaded function type>, const std::_Placeholder<1>&)’
16 | auto new_func = std::bind(f, std::placeholders::_1);
| ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
可以看到,std::bind 准确地说是一个函数模板。它要根据其参数进行模板实参推导,再替换模板形参进行实例化(Instantiation),产生和普通函数类似的汇编代码。std::bind 进行实例化的时候,函数 f 还没有进行重载解析,其类型为<unresolved overloaded function type>。std::bind 无法进行实例化。怎样修改可以解决这个问题呢?
可以利用第 6 个语境,也就是显示转换或 static_cast。重载解析会选择与它们的目标类型相匹配的版本。下面的代码会如期运行:
#include <iostream>
#include <functional>
void f()
{
std::cout << "f 1" << std::endl;
}
void f(int x)
{
std::cout << "f 2 " << x << std::endl;
}
int main()
{
auto new_func = std::bind((void(*)(int))f, std::placeholders::_1);
new_func(66);
}
这段代码输出:
f 2 66
还有一种更加巧妙的办法,依然是利用第 3 种语境。既然隐式实例化会进行模板实参推导,和重载解析相矛盾。为什么不直接解决这个矛盾,将隐式实例化改为显示实例化?来看下面的代码:
#include <iostream>
#include <functional>
void f()
{
std::cout << "f 1" << std::endl;
}
void f(int x)
{
std::cout << "f 2 " << x << std::endl;
}
int main()
{
auto new_func = std::bind<void(int)>(f, std::placeholders::_1);
new_func(66);
}
这段代码如期输出:
f 2 66
C++ 获取指定的重载函数地址的更多相关文章
- JAVA 获取指定网址的IP地址 实例
如今买票是一大难事,在高峰时段 打开12306网站,慢的像蜗牛,想到以前用修改hosts文件来登录Google(Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址 ...
- Windows系统调用架构分析—也谈KiFastCallEntry函数地址的获取
为什么要写这篇文章 1. 因为最近在学习<软件调试>这本书,看到书中的某个调试历程中讲了Windows的系统调用的实现机制,其中讲到了从Ring3跳转到Ring0之后直接进入了K ...
- 获取C++类成员虚函数地址
1.GCC平台 GCC平台获取C++成员虚函数地址可使用如下方法[1]: class Base{ int i; public: virtual void f1(){ cout<<" ...
- 函数用途:同一域名对应多个IP时,获取指定服务器的远程网页内容
<?php /************************ * 函数用途:同一域名对应多个IP时,获取指定服务器的远程网页内容 * 创建时间:2008-12-09 * 创建人:张宴(img. ...
- 旧书重温:0day2【4】动态获取函数地址
通过以上3篇文章的学习,我们已经可以获取到kernel32.dll的地址了下一步 我们就是获取几个重要的函数 1.GetProcAddress 2.LoadLibrary 有了这两个函数很多函数都可以 ...
- 【逆向篇】分析一段简单的ShellCode——从TEB到函数地址获取
其实分在逆向篇不太合适,因为并没有逆向什么程序. 在http://www.exploit-db.com/exploits/28996/上看到这么一段最简单的ShellCode,其中的技术也是比较常见的 ...
- 告别硬编码-发个获取未导出函数地址的Dll及源码
还在为找内核未导出函数地址而苦恼嘛? 还在为硬编码通用性差而不爽吗? 还在为暴搜内核老蓝屏而痛苦吗? 请看这里: 最近老要用到内核未导出的函数及一些结构,不想再找特征码了,准备到网上找点符号文件解析的 ...
- C#获取指定IP地址的数据库所有数据库实例名
/// <summary> /// 获取指定IP地址的数据库所有数据库实例名. /// </summary> /// <param name="ip" ...
- JAVA获取指定的类型的本机MAC地址
前面我们运维小伙在部署的时候,发现在真实服务器获取不到mac地址或者获取不到指定类型的mac地址,写程序记录如下 import com.google.common.base.Strings; impo ...
随机推荐
- GitHub Pages + Hexo搭建个人博客网站-github风格-采坑记录
目录 1.本机安装nodejs 2.github上创建仓库 3.安装hexo 4.hexo主题 5.配置主题 6.添加文章 7.使用分类和标签 8.增加文章目录 9.推送github 使用github ...
- Python入门-程序结构扩展
deque双端队列 #双端队列,就是生产消费者模式,依赖collections模块 from collections import deque def main(): info = deque((&q ...
- 关于javaweb学习终点的一些感悟
学习完javaweb后,自己做了一套管理项目,使用了mybatis,themeleaf和servlet.大致明白了servlet的真实应用场景. 说白了servlet就是用来指定浏览器url后面输入了 ...
- 51单片机I/O引脚IO口工作原理
51单片机I/O引脚IO口工作原理 一.51单片机管脚p0.p1.p2.p3口区别如下: 1.意思不同P0口作输出口用时,需加上拉电阻.P0口有复用功能.当对外部存储器进行读写操作时,P0口先是提供外 ...
- v-if 和 v-show 的区别
1.v-if && v-else && v-else-if <span v-if = "4 > 9">hello</span ...
- 2021牛客暑期多校训练营3 J 思维
传送门 J-Counting Triangles_2021牛客暑期多校训练营3 (nowcoder.com) 题目 Goodeat finds an undirected complete graph ...
- Vue 生产环境(production) 打开 调试工具的方法
总所周知,在production生产环境下,点击vue插件会显示, 并且按F12是没办法使用调试工具的,这样非常不方便 其实是有一个办法的,那就是复制下面的js,然后在控制台执行 代码中已经写清楚执行 ...
- 深入了解 TiDB SQL 优化器
分享嘉宾:张建 PingCAP TiDB优化器与执行引擎技术负责人 编辑整理:Druid中国用户组第6次大数据MeetUp 出品平台:DataFunTalk 导读: 本次报告张老师主要从原理上带大家深 ...
- XCTF练习题---WEB---disabled_button
XCTF练习题---WEB---disabled_button flag:cyberpeace{74bcfce0746d18dd8d560e0f0529a8cf} 解题步骤: 1.观察题目,打开场景 ...
- dfs深搜
一.01背包dfs //回溯法,01背包 #include<iostream> #include<algorithm> using namespace std; const i ...