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 ...
随机推荐
- 牛客网-剑指Offer 二维数组中的查找
题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...
- ccf201912-1 报数 C++代码实现
代码实现: #include<iostream> using namespace std; /*题目限制为三位数*/ /*思路: 1.用一个长度为4的数组(初值为0)保存每个人分别跳过了几 ...
- 移动端input输入框把页面顶起, 收起键盘页面复原不了问题
我相信大家平时也会遇到这种问题, 移动端 input 或者 textarea获取光标, 整个页面被顶起来, 键盘收起, 页面不复原的问题 ====>>>> 我这边提供两种解决 ...
- [源码解析] TensorFlow 分布式环境(8) --- 通信机制
[源码解析] TensorFlow 分布式环境(8) --- 通信机制 目录 [源码解析] TensorFlow 分布式环境(8) --- 通信机制 1. 机制 1.1 消息标识符 1.1.1 定义 ...
- 帝国CMS批量提取正文内容到简介
最近接到一个帝国CMS模板改版项目,自带的数据可能是采集的,以前的简介字段内容只截取了60个字,新模板的简介60字符太少了,不美观,想让简介都截取200个字,怎么批量修改呢,文章太多了手动改肯定不行, ...
- 学习day43
开始学习html知识
- vue3跳转路由3步曲
import { useRouter } from 'vue-router'; // 1. 引入路由export default { setup() { const $router ...
- 【面试普通人VS高手系列】b树和b+树的理解
数据结构与算法问题,困扰了无数的小伙伴. 很多小伙伴对数据结构与算法的认知有一个误区,认为工作中没有用到,为什么面试要问,问了能解决实际问题? 图灵奖获得者: Niklaus Wirth 说过: 程序 ...
- [linux tips] puppet client ssl 证书过期
问题: [root@control-01 .ssh]# puppet agent -tv Warning: Unable to fetch my node definition, but the ag ...
- Quantexa CDI(场景决策智能)Syneo平台介绍
Quantexa 大数据服务提供商, 使用实体解析, 关系分析和人工智能技术帮助客户进行数据处理和预防金融犯罪. 企业概览 2016年成立, 当前规模500人 服务特色是场景决策智能CDI(conte ...