【C++】C++11 ~ 包装器解析
🌈欢迎来到C++专栏~~包装器解析
- (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
- 目前状态:大三非科班啃C++中
- 🌍博客主页:张小姐的猫~江湖背景
- 快上车🚘,握好方向盘跟我有一起打天下嘞!
- 送给自己的一句鸡汤🤔:
- 🔥真正的大师永远怀着一颗学徒的心
- 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
- 🎉🎉欢迎持续关注!
文章目录
- 🌈欢迎来到C++专栏~~包装器解析
- 🥑 概念
- 🥑类型统一
- ✨例题:求解逆波兰表达式
- ✨包装器的意义
- bind 包装器😎
- 💥bind 绑定固定参数
- 💥调整传参顺序
- 💥bind包装器的意义
- 📢写在最后
🥑 概念
function
包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。
那么我们来看看,我们为什么需要function呢?
包装器定义式:
// 类模板原型如下
template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret
: 是被包装的可调用对象的返回值类型Args...
:是被包装的可调用对象的形参类型
function包装器可以对可调用对象进行包装,包括函数指针、函数名、仿函数(函数对象)、lambda表达式
int f(int a, int b)
{return a + b;
}struct Functor
{
public:int operator() (int a, int b){return a + b;}
};class Plus
{
public://静态 vs 非静态static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};int main()
{function<int(int, int)> f1 = f;f1(1, 2);function<int(int, int)> f2 = Functor();f2(1, 2);function<int(int, int)> f3 = Plus::plusi;f3(1, 2);//非静态成员函数 要 + 对象function<double(Plus, double, double)> f4 = &Plus::plusd;f4(Plus(), 1.1, 2.2);return 0;
}
注意事项:
- 取静态成员函数的地址可以不用取地址运算符“&”,但取非静态成员函数的地址必须使用取地址运算符“
&
” - 非静态成员函数在调用的时候要
+ 对象
,因为非静态成员函数的第一个参数是隐藏this指针,所以在包装时需要指明第一个形参的类型为类的类型。
🥑类型统一
对于以下函数模板useF:
- 传入该函数模板的第一个参数可以是任意的可调用对象,比如函数指针、仿函数、lambda表达式等
- useF中定义了静态变量count,并在每次调用时将count的值和地址进行了打印,可判断多次调用时调用的是否是同一个useF函数。
代码如下:
template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}
在不使用包装器,直接传入对象的时候,会实例化出三份
double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};
int main()
{//函数指针cout << useF(f, 11.11) << endl;//仿函数cout << useF(Functor(), 11.11) << endl;//lambda表达式cout << useF([](double d)->double{return d / 4; }, 11.11) << endl;return 0;
}
输出结果如下:
由于函数指针、仿函数、lambda表达式是不同的类型,因此useF函数会被实例化出三份,三次调用useF函数所打印count的地址也是不同的。
- 但实际这里根本没有必要实例化出三份useF函数,因为三次调用useF函数时传入的可调用对象虽然是不同类型的,但这三个可调用对象的返回值和形参类型都是相同的
- 这时就可以用包装器分别对着三个可调用对象进行包装,然后再用这三个包装后的可调用对象来调用useF函数,这时就只会实例化出一份useF函数
- 根本原因就是因为包装后,这三个可调用对象都是相同的function类型,因此最终只会实例化出一份useF函数,该函数的第一个模板参数的类型就是function类型的
包装后代码如下:
int main()
{// 函数指针function<double(double)> f1 = f;cout << useF(f1, 11.11) << endl;// 函数对象function<double(double)> f2 = Functor();cout << useF(f2, 11.11) << endl;// lamber表达式function<double(double)> f3 = [](double d)->double { return d / 4; };cout << useF(f3, 11.11) << endl;return 0;
}
✨例题:求解逆波兰表达式
题目地址:传送
解题思路:
- 首先定义一个栈,依次遍历所给字符串
- 遇到数字,直接入栈,遇到操作符,则从栈定抛出两个数字进行对应的运算,并将运算后得到的结果压入栈中
- 所给字符串遍历完毕后,栈顶的数字就是逆波兰表达式的计算结果
此处的包装器:
- 建立各个运算符与其对应需要执行的函数之间的映射关系,当需要执行某一运算时就可以直接通过运算符找到对应的函数进行执行
- 当运算类型增加时,就只需要建立新增运算符与其对应函数之间的映射关系即可(其他代码不用动)
class Solution {
public:int evalRPN(vector<string>& tokens) {stack<long long> st;map<string, function<long long(long long, long long)>> opfuncMap = {//自动构造pair ~ 初始化列表构造{"+", [](long long a , long long b){return a + b;}},{"-", [](long long a , long long b){return a - b;}},{"*", [](long long a , long long b){return a * b;}},{"/", [](long long a , long long b){return a / b;}},};for(auto& str : tokens){if(opfuncMap.count(str)){//操作符 :出栈(先出右,再出左)long long right = st.top();st.pop();long long left = st.top();st.pop();st.push(opfuncMap[str](left, right));}else{//操作数:入栈st.push(stoll(str));}}return st.top();}
};
✨包装器的意义
- 将可调用对象的类型进行统一,便于我们对其进行统一化管理。
- 包装后明确了可调用对象的返回值和形参类型,更加方便使用者使用。
bind 包装器😎
bind 是一种函数包装器,也叫做适配器。它可以接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表,C++ 中的 bind 本质还是一个函数模板
bind函数模板的原型如下:
template <class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);
template <class Ret, class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);
模板参数说明:
fn
:可调用对象args...
:要绑定的参数列表:值或占位符
调用bind的一般形式
auto newCallable = bind(callable, arg_list);
callable
:需要包装的可调用对象
newCallable
:生成的新的可调用对象
arg_list
:逗号分隔的参数列表,对应给定的 callable 的参数,当调用 newCallable时,newCallable 会调用 callable,并传给它 arg_list 中的参数
_1 _2 ...
是定义在placeholders命名空间中,代表绑定函数对象的形参;_1代表第一个形参,_2代表第二个形参 …
举例:
using namespace placeholders;
int x = 2, y = 10;
Div(x, y);auto bindFun1 = bind(Div, _1, _2);
💥bind 绑定固定参数
原本传入的参数要求是要3个,现在只需要输入两个参数,因为绑定了固定的函数对象
using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};int main()
{//function<int(Sub, int, int)> fsub = &Sub::sub;function<int(int, int)> fsub = bind(&Sub::sub, Sub(), _1, _2);
}
想把Mul函数的第三个参数固定绑定为1.5,可以在绑定时将参数列表的placeholders::_3设置为1.5。比如:
int Mul(int a, int b, int rate)
{return a * b * rate;
}int main()
{function<int(int, int)> fmul = bind(Mul, _1, _2, 1.5);
}
💥调整传参顺序
对于 Sub 类中的 sub 函数,因为 sub 的第一个参数是隐藏的 this 指针,如果想要在调用 sub 时不用对象进行调用,那么可以将 sub 的第一个参数固定绑定为一个 Sub 对象:
using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{//绑定固定参数function<int(int, int)> func = bind(&Sub::sub, Sub(), _1, _2);cout << func(1, 2) << endl; return 0;
}
此时调用对象时就只需要传入用于相减的两个参数,因为在调用时会固定帮我们传入一个匿名对象给 this 指针。
如果想要将 sub 的两个参数顺序交换,那么直接在绑定时将 _1 和_2 的位置交换一下就行了:
using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{//绑定固定参数function<int(int, int)> func = bind(&Sub::sub, Sub(), _2, _1);cout << func(1, 2) << endl; return 0;
}
其原理:第一个参数会传给_1,第二个参数会传给 _2,因此可以在绑定时通过控制 _n 的位置,来控制第 n 个参数的传递位置
💥bind包装器的意义
- 将一个函数的某些参数绑定为固定的值,让我们在调用时可以不用传递某些参数。
- 可以对函数参数的顺序进行灵活调整。
📢写在最后
kd
相关文章:

【C++】C++11 ~ 包装器解析
🌈欢迎来到C专栏~~包装器解析 (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort目前状态:大三非科班啃C中🌍博客主页:张小姐的猫~江湖背景快上车🚘,握好方向盘跟我有一起打天下嘞!送给自己的一句鸡汤&a…...

SpringBoot整合(三)SpringBoot发送邮件
使用SpringBoot发送邮件 邮件发送其实是一个非常常见的需求,用户注册,找回密码等地方,都会用到,Spring Boot 中对于邮件发送,提供了相关的自动化配置类,使得邮件发送变得非常容易。 1、前置工作 目前国内…...

【docker知识】联合文件系统(unionFS)原理
一、说明 Docker CLI 操作起来比较简单——您只需掌握Create、Run、InspPull和Push容器和图像,但是谁想过Docker 背后的内部机制是如何工作的?在这个简单的表象背后隐藏着许多很酷的技术, UnionFS(统一文件系统)就是其…...

使用Lame库实现wav、pcm转mp3
文章目录 前言 一、Lame库是什么? 二、使用步骤 0.创建native项目 1.下载Lame库 2.pcm转MP3 3.wav转MP3 4、native方法如下 三、注意 总结 前言 因为使用android录音后生成的文件是wav或者pcm格式,项目要求最后的文件需要是mp3格式,于…...

c++11 标准模板(STL)(std::multimap)(三)
定义于头文件 <map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class multimap;(1)namespace pmr { template <class Key, class T…...

【报复性赚钱】2023年5大风口行业
今天就来和大家分享一下,在时代的洪流下,普通人如何顺应大势抓住机遇! 实现人在风口上,猪都会飞起来。 根据对市场的观察及各平台数据分析结果,结合国家政策和经济专家的分析,小编预测了2023年将会迎来大…...

单目相机、双目相机和RGB-D相机学习笔记(一些视频和博文网址)
目录1. 单目相机1.1 摄像头原理1.2 单目相机的标定2 双目相机2.1 双目相机定位原理2.2 双目相机的缺陷3 RGB-D相机3.1 深度相机结构光原理3.2 RGB-D相机的应用1. 单目相机 1.1 摄像头原理 视频网址:【全网最详细】摄像头原理分析(约25分钟课程…...

word和wps添加mathtype选项卡
word或wps添加mathtype选项卡 前提 安装好word或wps安装好mathtype 步骤 确认word或wps具体安装位置确认word或wps位数为32位还是64位复制mathtype中的MathPage.wll文件和MathType Commands 2016.dotm文件到STARTUP位置添加受信任位置添加加载项 安装位置 通过开始页面&a…...

获取成员userID
文章目录一、简介二、获取token1、获取秘钥2、获取Token三、获取部门数据1、获取部门列表2、获取子部门ID列表3、获取单个部门详情四、获取成员信息1、读取成员2、获取部门成员3、获取部门成员详情一、简介 同步数据到企微: 企业如果需要从自有的系统同步通讯录到…...

DOM编程-显示网页时钟
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>显示网页时钟</title> </head> <body bgcolor"antiquewhite"> <script type"text/javascrip…...

浅谈保护数据的加密策略
加密是一种将信息从可读格式转换为混乱字符串的技术。这样做可以防止数据传输中的机密数据泄露。文档、文件、消息和所有其他形式的网络通信都可以加密。加密策略和身份验证服务的结合,还能保障企业机密信息只对授权用户开启访问权限。常见的数据加密包括以下两种&a…...
Java中String,StringBuffer和StringBuilder
String类 我们在定义string变量时 常常写 String str "hello word"; 这样的代码,看起来和int a 0; 是一样的声明方式, 但其实两者是不同的, int 是java中定义的基本数据类型, 而String是一个类,是一个特殊的类,可以像基本数据类型一样直接赋…...

华为认证常见技术问答整理:什么是Datacom认证?
一、关于Datacom认证Q:什么是Datacom认证?A:Datacom,即DatacomCommunication的缩写,中文为“数据通信”,属于ICT技术架构认证类别(华为认证包含ICT技术架构认证、平台与服务认证和行业ICT认证三…...
Read book Netty in action (Chapter II) (Netty Introduction)
前言 支持15W的并发客户端,我们应该视为理所当然的事情,很多公司甚至能够支撑更多,例如我们熟知的 BAT,当几年前双十一的夜晚,并发量是不可估计的。还有春节的时候购票的时候的并发。作为一个优秀的开发人员ÿ…...
python--route
routes是用python重新实现的Rails routes系统,用于将url映射到应用程序的actions ,并反过来生成url 它也是在openstack实现restful通信的方式,它被用来做将 URL 映射为 App 的 action,以及为 App的action 产生 URL 两个重要的方法…...

java面试中被问到项目中的难点,怎么回答
java面试中被问到项目中的难点,怎么回答回答步骤举例说明回答步骤 回答这个问题的方法取决于你的项目的类型和难度。 但是,一般来说,你可以遵循以下步骤来回答这个问题: 描述你的项目:首先简要描述你的项目的类型和目…...

【速通版】吴恩达机器学习笔记Part1
准备速通一下吴恩达的机器学习 很快做个笔记5.2.3 监督学习 part 2_哔哩哔哩_bilibili 目录 1.概述(P1-P3) 2.supervised learning:(P4,P5) regression: classification 3.unsupervised learning (P6- 1.聚类算…...

面试(九)小米C++开发一面 21.11.02
1、局部变量与全局变量的区别?可以同名嘛? 首先是作用域: 局部变量只在变量声明的代码块范围内生效 全局变量在其声明后的所有位置都能访问到 在局部变量与全局变量同名的情况下,全局变量会被屏蔽掉,只会使用局部变量的内容 2、extern 当在a.c中想要使用b.c中的函数fu…...

儿童书写台灯哪个牌子比较好?2023儿童护眼台灯分享
现在儿童的近视率高达52.7%,有科技水平的提高和电子产品的普及,近视率逐年攀升,出现低龄化现象,调查结果显示,其中6岁儿童达到14.3%,小学生为35.6%。初中生71.1%,高中生高达80.5%,可…...

市场调研计划书如何写?
想要做好一个产品,市场调研是必不可少的一步,也是第一步,那么如何进行市场调研呢?以下是我整理的一份市场调研计划书,希望能够帮助到大家!!! 一、文档版本控制 主要记录文档的版本…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...