【C++11】std::function 包装器(又叫适配器),std::bind 绑定
文章目录
- std::function 包装器
- 1. 使用方法
- 2. 包装器的应用场景:题目 - - 逆波兰表达式求值
- 3. 成员函数 和 static 静态成员函数 使用 包装器
- std::bind 适配器绑定
- 1. 使用方法
- 2. 调整参数 顺序
- 3. 指定参数 / 参数个数的调整
std::function 包装器
std::function 包装器,也叫做 适配器。
头文件如下:#include<functional>
类模板原型如下:
template <class T> function; // undefined template <class Ret, class... Args> class function<Ret(Args...)>;
作用是 对 可调用对象类型(callable object) 进行 再封装适配,
C++ 中的 function 本质上是一个类模板。
首先我们如果针对相同的功能实现,可以选用下面的方法,但他们都有不同的类型:
- 函数指针:类型比较难写
- 仿函数(类中定义 operator()())
- lambda:从底层角度来说其实也是仿函数
举例下面两个代码,都实现了数据的相加,使用方法是一样的,但是他们的类型完全不同。
#include<map>
#include<functional> // 包装器的头文件// 函数写法
int func(int a, int b)
{cout << "int f(int a, int b)" << endl;return a + b;
}// 仿函数写法
struct Functor
{
public:int operator() (int a, int b){cout << "int operator() (int a, int b)" << endl;return a + b;}
};
即使他们的 返回值、参数列表 都相同。
如果有需求要这两个 函数 和 仿函数 声明出一个统一的类型,即 使用 map 的数据结构,把他们放入其中管理,他们类型完全不同,怎么写呢?
// map<string, xxx> // 需要声明一个可调用的类型,xxx 这个类型怎么写呢
// int(*pf1)(int,int) = func; // 这样写就没法和别的统一一个类型,无法和 map 规定的模板类型匹配
用 包装器 就可以适配出可调用类型。
1. 使用方法
包装器定义格式:function<返回值(参数列表)> func = 可调用对象
-
可适配对象包括:
函数指针 / 函数名、仿函数、lambda -
即
function<返回值(参数列表)>
这个类型,就可以标识一系列 相同返回值、参数列表 的可适配对象了。
🌰具体用法如下图举例:
- 直接定义使用:
int main()
{function<int(int, int)> f1 = func;function<int(int, int)> f2 = Functor(); // 经过包装后这俩对象的类型就是一样的了function<int(int, int)> f3 = [](int a, int b) {cout << "[](int a, int b) {return a + b;}" << endl;return a + b;};cout << f1(1, 2) << endl; // int f(int a, int b)cout << f2(10, 20) << endl; // int operator() (int a, int b)cout << f3(100, 200) << endl; // [](int a, int b) {return a + b;}return 0;
}
- 做模板参数使用
int main()
{map<string, function<int(int, int)>> opFuncMap;opFuncMap["函数指针"] = func;opFuncMap["仿函数"] = Functor();opFuncMap["lambda"] = [](int a, int b) {cout << "[](int a, int b) {return a + b;}" << endl;return a + b;};cout << opFuncMap["lambda"](1, 2) << endl; // [](int a, int b) {return a + b;}return 0;
}
2. 包装器的应用场景:题目 - - 逆波兰表达式求值
👉🔗leetcode【150.逆波兰表达式求值】
给你一个字符串数组 tokens,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式,返回一个表示表达式值的整数。注意:
- 有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是向零截断,表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例:
- 输入:tokens = [“4”,“13”,“5”,“/”,“+”]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
#include<stack>
class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> st;map<string, function<int(int, int)>> opfuncMap ={{"+",[](int x, int y) {return x + y; }},{"-",[](int x, int y) {return x - y; }},{"*",[](int x, int y) {return x * y; }},{"/",[](int x, int y) {return x / y; }}};for (auto str : tokens) // 遍历给出的 逆波兰表达式 字符串{// 看是否是算数操作符if (opfuncMap.count(str)) // 是操作符,将 st 中最 top 的两个数据 pop 出来,用 opfuncMap[str] 函数进行计算,把结果 push 进 st{int right = st.top();st.pop();int left = st.top();st.pop();st.push(opfuncMap[str](left, right));}else // 不是操作符,即是操作数,push 到 st 中,等待操作符出现后进行计算{st.push(stoi(str)); // string 转 int}}return st.top();}
};
3. 成员函数 和 static 静态成员函数 使用 包装器
以这个类举例,里面有一个静态成员函数 plusi,一个非静态成员函数plusd:
class Plus
{
public:Plus(int rate = 2):_rate(rate) {}static int plusi(int a, int b) {return a + b;}double plusd(double a, double b) {return (a + b) * _rate;}private:int _rate = 2;
};
静态成员函数:
- 函数名就可以代表函数指针,不需要取地址。
非静态成员函数:
- 需要写成
&函数名
- 因为非静态成员函数还有一个隐藏的参数 this*,所以定义时还需要多声明一个参数,即非静态成员函数所属类的类型名
- 调用的时候也需要多传一个 所属类的 对象\匿名对象
int main()
{// 静态成员函数,函数名就行function<int(int, int)> f1 = Plus::plusi;// 非静态成员函数,&函数名,另 多一个参数function<double(Plus, double, double)> f2 = &Plus::plusd; // 这里其实是,Plus是类型,是通过我们创建的对象调用的 plusd // 调用cout << f1(1, 2) << endl;cout << f2(Plus(), 20, 20) << endl; // 非静态,多传一个匿名对象cout << f2(Plus(3), 20, 20) << endl; // 还可以给匿名对象一个构造值Plus pl(3); // 给有名对象当然也okcout << f2(pl, 20, 20) << endl;return 0;
}// 包装器的本质也是仿函数
至于,包装器定义非静态函数成员,传模板参数时,多传一个类类型作为参数,为了填补 this*,为啥不直接写成指针的格式?
因为,如果这里声明成指针,我们使用的时候就不能用匿名对象(因为匿名对象是右值不能取地址)这样调用了,所以把这类型包装器第一个参数设计成指针的写法不方便。
// 不推荐这样写 ~~~
function<double(Plus*, double, double)> f3 = &Plus::plusd;
Plus plus();
cout << f3(&plus, 5, 3) << endl;
std::bind 适配器绑定
std::bind 函数定义在头文件中,是一个函数模板,原型如下:
template <class Fn, class... Args> /* unspecified */ bind (Fn&& fn, Args&&... args);// with return type (2) template <class Ret, class Fn, class... Args> /* unspecified */ bind (Fn&& fn, Args&&... args);
它就像一个函数包装器,接受一个可调用对象,生成一个新的可调用对
象来 适应和调整 原对象的 参数列表(个数、顺序)
- 一般而言,我们用它可以把一个原本接收 N 个参数的函数 fn,通过绑定一些参数,返回一个接收 M 个参数的新函数(M 可以大于 N,但这么做没什么意义)。
- 同时,使用 std::bind 函数还可以实现参数顺序调整等操作。
1. 使用方法
调用 bind 的一般形式:
auto newCallable = bind(callable, arg_list);
- newCallable :本身是一个可调用对象
- arg_list : 是一个逗号分隔的参数列表,对应给定的 callable 的参数。
当我们调用 newCallable 时,newCallable 会调用 callable,并传给它 arg_list 中的参数。
占位符:
arg_list 中的参数可能包含形如 xxx_n
的名字,其中 n 是一个整数,这些参数就是 占位符,表示 newCallable 的参数,它们占据了传递给 newCallable 的参数的 “位置”。
数值 n 表示生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2 为第二个参数,以此类推。
综上,我们能知道 bind 实现的是对绑定函数参数的调整。
- 当 bind() 中 arg_list 使用占位符的时候,占位符 可以调整原函数 参数顺序;
- 当 arg_list 中传入具体参数的时候,就相当于原函数该参数位置被 指定,也就相当于在调用调整后的函数时,可以少传参数了,也就是对参数个数的调整。
2. 调整参数 顺序
使用占位符用来进行顺序的调整
🌰举例使用如下:
void print(int a, int b)
{cout << a << endl;cout << b << endl;
}
int main()
{print(10, 20);// _1 表示第一个参数,_2 是第二个function<void(int,int)> Rprint = bind(print, placeholders::_2, placeholders::_1); //auto Rprint = bind(print, placeholders::_2, placeholders::_1); Rprint(10, 20); // 20 10return 0;
}
还是之前 给逆波兰表达式求值的代码,在用法上面扩展一下(只为使用语法):
int plus(int a, int b)
{return a + b;
}class Sub{
public:Sub(int rate = 3):_rate(rate) {}int func(int a, int b) {return (a - b)*_rate;} // 这里 func 加 this* 有三个参数int operator()(int x, int y) {return x * y;}
private:int _rate;
};int mul(int x, int y)
{return x * y;
}class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> st;map<string, function<int(int, int)>> opfuncMap ={{"+",[](int x, int y) {return x + y; }}, // lambda 表达式{"-",Sub()}, // 可以接收仿函数{"*",mul}, // 可以接受函数指针{"/",[](int x, int y) {return x / y; }},{"&",bind(&Sub::func,Sub(3),placeholders::_1,placeholders::_2)}}; // 别的参数都是两个,最后一个恰好是三个并且有一个能绑死,就刚好用在这里!// 代码略// 遍历给出的 逆波兰表达式 字符串// 看是否是算数操作符// 是操作符,将 st 中最 top 的两个数据 pop 出来,用 opfuncMap[str] 函数进行计算,把结果 push 进 st// 不是操作符,即是操作数,push 到 st 中,等待操作符出现后进行计算// return st.top();}
};
3. 指定参数 / 参数个数的调整
auto newCallable = bind(callable, arg_list);
arg_list 部分传入具体的参数,就代表该位置被 指定 了,调用调整后的函数就可以少传部分参数了。
🌰举例使用如下:
/*
class Sub{
public:Sub(int rate = 3):_rate(rate) {}int func(int a, int b) {return (a - b)*_rate;} // 这里 func 加 this* 有三个参数int operator()(int x, int y) {return x * y;}
private:int _rate;
};
*/int main()
{// 不调整参数时function<int(Sub, int, int)> fsub = &Sub::func; // 要加上取地址符cout << fsub(Sub(1), 10, 20) << endl;// bind 第一个参数function<int(int, int)> fsub2 = bind(&Sub::func, Sub(3), placeholders::_1, placeholders::_2); cout << fsub2(10, 20) << endl;// bind 第二个参数:注意function<int(Sub, int)> fsub3 = bind(&Sub::func, placeholders::_1, 100, placeholders::_2); cout << fsub3(Sub(2), 20) << endl;return 0;
}
🥰如果本文对你有些帮助,请给个赞或收藏,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 欢迎评论留言~~
相关文章:
【C++11】std::function 包装器(又叫适配器),std::bind 绑定
文章目录 std::function 包装器1. 使用方法2. 包装器的应用场景:题目 - - 逆波兰表达式求值3. 成员函数 和 static 静态成员函数 使用 包装器 std::bind 适配器绑定1. 使用方法2. 调整参数 顺序3. 指定参数 / 参数个数的调整 std::function 包装器 std::function 包…...
Linux系统编程系列之线程
一、什么是线程 线程(Thread)是计算机中的基本执行单元,是操作系统调度的最小单位。线程是进程内的一个独立执行流程,一个进程可以包含多个线程,这些线程共享进程的资源,但每个线程都有自己的独立栈空间以及…...
CV面试知识点总结
一.卷积操作和图像处理中的中值滤波操作有什么区别? 1.1卷积操作 卷积操作是一种线性操作,通常用于特征的提取,通过卷积核的加权求和来得到新的像素值。1.2中值滤波 原文: https://blog.csdn.net/weixin_51571728/article/detai…...
Centos一键安装、切换各版本JDK
查看服务中的安装的jdk rpm -qa | grep java获取jdk各版本信息 yum -y list java*查看指定版本 yum -y list java*|grep 1.8安装jdk yum install java-11-openjdk当服务器中有多个版本jdk,切换指定jdk版本 alternatives --config java按照提示输入编号即可切换&…...
JavaWeb项目:smbms(mysql)
1.准备工作,创建数据库 CREATE DATABASE smbms;USE smbms;CREATE TABLE smbms_address (id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID,contact VARCHAR(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 联系人姓名,addressDesc VARCHAR(50) COLLATE u…...
shell脚本的多线程介绍
shell脚本的多线程介绍 shell脚本中,实现多线程可以使用以下方法: 1)使用&符号 在Shell中,可以使用&符号将命令放在后台执行,这样就可以同时执行多个命令。例如: #!/bin/bash command1 & #…...
周记之反思
9.25 这篇总结我承认,是在26号上午写的,那昨天晚上又聊天了,但是对比之前来说好很多了,所以26号上午也就是今天我起了个大早,然后把昨天的尾巴收了一下,没收完,先说说成果: 完成了…...
信创办公–基于WPS的EXCEL最佳实践系列 (数据整理复制粘贴)
信创办公–基于WPS的EXCEL最佳实践系列 (数据整理复制粘贴) 目录 应用背景操作步骤1、数据查找与替换2、复制或粘贴数据3、使用自动填充工具4、将数据拆分到多列5、应用数字格式 应用背景 数据的整理复制粘贴等在日常的工作中经常使用。本章内容主要学习…...
二极管的直流等效电路和微变等效电路
二级管的主要参数 1.IF(最大整流的电流) 二极管长期工作做能够通过电流的平均最大值:物理意义:功率电流值。 2.UR 二极管最高反向工作电压 需要留有裕度,通常能达到一半的裕度;UR不能等于UBR。 3.IR 未击穿…...
Python无废话-基础知识字典Dictionary详讲
“字典Dictionary” 是一种无序、可变且可嵌套的数据类型,用于存储键值对。字典使用花括号{}来定义,并用逗号分隔键值对。本文对字典常使用方法,创建字典、添加字典、删除字典、如何获取字典做了知识归纳。 字典有以下几个特征: …...
ChatGPT多模态升级,支持图片和语音,体验如何?
一、前言 9 月 25 日,ChatGPT 多模态增加了新的语音功能和图像功能。这些功能提供了一种新的、更直观的界面,允许我们与 ChatGPT 进行语音对话或展示我们正在谈论的内容。 ChatGPT 现在可以看、听、和说话了,而不单单是一个文本驱动的工具了。…...
(SAR)Sentinel-1影像自动下载
基于ASF网站提供的python代码,实现Sentinel-1影像的自动下载; 1、登录ASF网站 登录Sentinel-1影像ASF网站:https://search.asf.alaska.edu/; 点击网站最右侧Sign in图标,进行用户注册; 注册完用户之后&…...
设计模式10、外观模式Facade
解释说明:外观模式(Facade Pattern)又称为门面模式,属于结构型模式 Faade 为子系统中的一组接口提供了一个统一的高层接口,该接口使得子系统更加容易使用 外观(Facade)角色:为多个子系统对外提供…...
华为数通方向HCIP-DataCom H12-831题库(单选题:181-200)
第181题 以下关于OSPF的5类LSA中的转发地址(ForwardingAddress,FA) 的描述,正确的是哪一项? A、当FA地址为0.0.0.0时,收到该LSA的路由器认为到达目的网段的数据包应该发往对应的ABR,因此将到达ABR的下一跳地址作为这条外部路由的下一跳 B、当FA地址为0.0.0.0时,收到该LS…...
Java 中的参数传递方式
Java 中的参数传递方式通常被称为“值传递”,这意味着在方法调用时,实际上传递给方法的是变量的副本,而不是变量本身。尽管这被广泛称为“值传递”,但需要注意的是,这并不意味着 Java 不支持引用传递。事实上ÿ…...
从0开始python学习-27.selenium 简单登录页面脚本
url https://test.com.cn/login driver.get(url)# 获取登录页面需要输入账号密码进行模拟登录操作 user driver.find_element(By.XPATH,//*[id"username"]).send_keys(username) pwd driver.find_element(By.XPATH,//*[id"selfpwd"]).send_keys(123456)…...
华为智能企业上网行为管理安全解决方案(2)
本文承接: https://blog.csdn.net/qq_37633855/article/details/133339254?spm1001.2014.3001.5501 重点讲解华为智能企业上网行为管理安全解决方案的部署流程。 华为智能企业上网行为管理安全解决方案(2) 课程地址方案部署整体流程组网规划…...
【python海洋专题九】Cartopy画地形等深线图
【python海洋专题九】Cartopy画地形等深线图 水深图基础差不多了,可以换成温度、盐度等 本期加上等深线 本期内容 1:地形等深线 cf ax.contour(lon, lat, ele[:, :], levelsnp.linspace(-9000,-100,10),colorsgray, linestyles-,linewidths0.25, t…...
Java后端模拟面试,题集①
1.Spring bean的生命周期 实例化 Instantiation属性赋值 Populate初始化 Initialization销毁 Destruction 2.Spring AOP的创建在bean的哪个时期进行的 (图片转载自Spring Bean的完整生命周期(带流程图,好记)) 3.MQ如…...
UE5.1编辑器拓展【二、脚本化资产行为,快速更改资产名字,1.直接添加前缀或后缀2.通过资产类判断添加修改前缀】
目录 了解相关的函数 第一种做法:自定义添加选择资产的前缀或后缀 代码 效果 第二种做法:通过映射来获取资产类型添加前缀和修改前缀 映射代码 代码 效果 在之前一章中,我们创建了插件,用来扩展编辑器的使用: …...
短期风速预测|LSTM|ELM|批处理(matlab代码)
目录 1 主要内容 LSTM-长短时记忆 ELM-极限学习机 2 部分代码 3 程序结果 4 程序链接 1 主要内容 该程序是预测类的基础性代码,程序对河北某地区的气象数据进行详细统计,程序最终得到pm2.5的预测结果,通过更改数据很容易得到风速预测结…...
【LeetCode热题100】--102.二叉树的层序遍历
102.二叉树的层序遍历 广度优先搜索: 我们可以想到最朴素的方法是用一个二元组 (node, level) 来表示状态,它表示某个节点和它所在的层数,每个新进队列的节点的 level 值都是父亲节点的 level 值加一。最后根据每个点的 level 对点进行分类&…...
第44节——redux store
一、概念 Redux 是一个用于管理 JavaScript 应用状态的库。在 Redux 中,整个应用的状态都存储在一个对象中,称为 store。 Store 实际上是一个 JavaScript 对象,它存储了整个应用的状态。它是唯一的,意味着应用中只有一个 store。…...
【2023年11月第四版教材】第17章《干系人管理》(第二部分)
第17章《干系人管理》(第二部分) 4 过程1-识别干系人4.1 数据收集★★★4.3数据分析4.4 权力利益方格4.5 数据表现:干系人映射分析和表现★★★ 5 过程2-规划干系人参与5.1 数据分析5.2 数据表现★★★5.2.1 干系人参与度评估矩阵★★★ 5.3 …...
含分布式电源的配电网可靠性评估(matlab代码)
目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考《基于仿射最小路法的含分布式电源配电网可靠性分析》文献方法,通过概率模型和时序模型分别进行建模,实现基于概率模型最小路法的含分布式电源配电网可靠性评估以及时序模型…...
react的组件
组件 组件是用来实现局部功能的代码和资源的集合(html/css/js),用来复用代码。 react中分为函数式组件和类式组件。函数式组件就是一个函数,函数的返回值就是组件的视图内容。类式组件就是通过class关键字创建的类,类…...
低功耗引擎Cliptrix为什么可以成为IOT的高效能工具
在万物互联的时代,现代人已普遍接受电视、音箱等电器设备具备智能化能力,也是在这个趋势下,我们身边越来越多的iOT设备联网和交互成为刚需。 但iot设备也面临到一些非常显著的痛点,例如iot设备的内存、处理器等核心元件无法与手机…...
深入学习git
1、git原理及整体架构图 一些常用的命令 git add . 或 git add src/com/ygl/hello/hello.java 指定文件 git commit . 或 git commit src/com/ygl/hello/hello.java 指定文件 git push origin 分支名称 2、git stash的应用场景 场景一:你正在当前分支A开发&…...
第9章 Mybatis
9.1 谈谈你对Mybatis的理解 难度:★★ 重点:★★ 白话解析 说清楚Mybatis是什么,它的工作流程,然后再对比一下Hibernate就好了。 1、Mybatis是什么:它一个半自动ORM框架,它底层把JDBC那套加载驱动、创建连接、创建statement等重复性的硬编码全部给你封装好了,程序员只…...
隐蔽通信论文复现
文章目录 前言一、Limits of Reliable Communication with Low Probability of Detection on AWGN Channels摘要introduction 前言 本文准备先考虑隐蔽中通信经典的Alice, Bob, Willie三点模型, 总结出其中的经典套路 一、Limits of Reliable Communication with Low Probabil…...
景安wordpress主机/盘古百晋广告营销是干嘛
我们可能会有一个疑惑,那就是:当我们在本地(自己的电脑)安装完 MySQL 之后,也创建了很多的数据库啊、表啊,也存储了很多的数据啊,但是这些内容都存储到哪里了呢?特别是,当…...
我的世界找建筑网站/网络营销推广方案设计
近日,有媒体爆料大疆无人机去年全年销售收入达10亿美元(约64亿元),净利润增长至2.5亿美元,预计今年净利将达到4.6亿美元。而大疆科技在2012年时销售收入仅为2600万美元,利润不过800万美元,增长速度惊人。大疆科技当前估…...
网站建设中效果/重庆网站建设维护
题目: 给定一个正整数n,一定存在若干整数平方和为该正整数,求满足该条件的最小整数个数。平方数为(1,4,9,16......),使其和为n。例如给定n12,则返回3&#x…...
做设计都有什么网站/快速排名教程
使用终端登录Linux后使用vim编辑文本,这时在默认设置下载插入模式使用小键盘会插入一些非数字的字符。 更改的方法: 1.在文件(file)-- > 默认设置属性(Default setting properties)–> 终端(Termin…...
wordpress链接里的图像地址/郑州seo技术服务顾问
https://blog.csdn.net/p942005405/article/details/84764104 评论区: 上面的所有结论都是基于什么得到的?真是武断!!STL的实现版本很多,VS、GCC版本不同,实现都不同,建议翻看STL源代码。 新版…...
网站的图片滚动怎么做的/爱站网长尾关键词挖掘查询工具
针对每个测试需求设计测试用例,测试用例设计的遵守的原则: 1.每一个测试需求至少有一个测试用例与之对应; 2. 每个测试用例包含的测试步骤尽量不要超过10个;如果过多就进行拆分; 3. 每一步step只包含有一种情况&#x…...