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 ...
随机推荐
- java中final变量的用法
4.4 final变量 final变量的数值不能在初始化之后进行改变(你希望a=3,有很多用到a的场合, 你当然不能在程序中就用3来代替a). 比如: final int h = 0; 想像有一 ...
- rem,px,em最大的区别;
px:px像素(Pixel).相对长度单位.像素px是相对于显示器屏幕分辨率而言的.移动端的分辨率很多.所以px不适用移动端em:em的值不固定:其长度继承父级元素的字体大小rem:相对于根元素htm ...
- Blazor组件自做五 : 使用JS隔离封装Google地图
Blazor组件自做五: 使用JS隔离封装Google地图 运行截图 演示地址 正式开始 1. 谷歌地图API 谷歌开发文档 开始学习 Maps JavaScript API 的最简单方法是查看一个简 ...
- 安装 UE 源码版
# 安装 UE 源码版 ## 下载安装包 > - 先去 Github 找 UE 官方开源的引擎组(这个需要申请加入) > - 加入后找到开源的源码版项目下载 zip 到本地 > - ...
- php进制转换
前端html页面代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...
- Java学习day18
学习了三种简单的布局结构 做了一个简单的多按键窗口 Panel无法单独存在而显示出来,需要借助一个容器,例如Frame 明天学习输入框监听和画笔
- 2021-01-25 cf #697 Div3 C题(超时,换思路减少复杂度)
题目链接:https://codeforces.com/contest/1475/problem/C 题意要求:需组成的2对,男的序号不能重,女的序号不能重 比如这例 输入: 行1--测试个数 行1` ...
- Java-GUI编程之ImageIO的使用
在实际生活中,很多软件都支持打开本地磁盘已经存在的图片,然后进行编辑,编辑完毕后,再重新保存到本地磁盘.如果使用AWT要完成这样的功能,那么需要使用到ImageIO这个类,可以操作本地磁盘的图片文件. ...
- Dubbo 学习笔记
分布式基础理论 1. 什么是分布式系统? 分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个系统 2. 应用架构演变 单一应用架构 当网站流量很小时,只需一个应用,将所有功能都部署在一起 ...
- 国产化之银河麒麟.netcore3.1访问https服务的两个问题
背景 某个项目需要实现基础软件全部国产化,其中操作系统指定银河麒麟,数据库使用达梦V8,CPU平台的范围包括x64.龙芯.飞腾.鲲鹏等. 考虑到这些基础产品对.NETCore的支持,最终选择了3.1版 ...