C++11——包装器


该篇为lambda表达式的延申,请在熟知lambda表达式的基础上阅读该文章

一文详解C++11lambda表达式icon-default.png?t=N7T8https://blog.csdn.net/qq_74260823/article/details/134839319?spm=1001.2014.3001.5501


 包装器的由来

这同样是一个不属于C++原始风味的语法

我们在lambda表达式中讲到过,就算lambda表达式是相同的,其类型也不同,不能进行相互赋值等操作。而这还只是在lambda表达式之间,如果有一个仿函数和一个lambda表达式,就算其功能相同,我们想把他们联系起来,那也堪比登天一般难。

//仿函数
struct plus
{int operator()(int num1, int num2){return num1 + num2;}
};int main()
{//lambda表达式auto lambda_plus = [](int a, int b) {return a + b; };//两者功能相同,却不能相互联系lambda_plus = plus();
}

就好比……

这就导致在很多应用场景,lambda表达式和仿函数使用起来是非常不便的。比如之前我们大量进行回调函数时,会采用函数指针的方式,构建一个函数指针数组,调用时按其对应的下标调用即可。但是lambda表达式则不然,每一个lambda表达式类型都不同,我们没办法去开辟一个定类型的数组,也就意味着传统的函数指针的方式是不可行的。

 于是,带着这个使命,包装器便产生了


包装器

包装器的作用是将函数,仿函数,lambda表达式的类型统一,使其可以相互联系,相互转化

包装器的定义

包装器在头文件functional中,其定义长这个鬼样子

 其中,ret代表返回值的类型,而Args代表参数包的类型。

直接来看例子吧,以加法的函数为例

也就是说,在function的模板里,第一个参数是返回值的类型,随后在括号里依次传入函数参数列表的类型,一一对应

而后赋值的时候,只需要让其等于已经定义过的可调用对象,便完成了赋值,可以正常进行使用。

包装器的类型

还是刚刚的例子,我们分别包装函数,函数指针,仿函数,lambda表达式,看看其类型的情况 

//函数
int normal_plus(int num1,int num2)
{return num1 + num2;
}//仿函数
struct functor_plus
{int operator()(int num1, int num2){return num1 + num2;}
};int main()
{//lambda表达式auto lambda_plus = [](int a, int b) {return a + b; };//使用包装器包装函数function<int(int, int)> function1 = normal_plus;//使用包装器包装函数指针function<int(int, int)> function2 = &normal_plus;//使用包装器包装仿函数function<int(int, int)> function3 = functor_plus();//使用包装器包装lambda表达式function<int(int, int)> function4 = lambda_plus;//其类型都为class std::function<int __cdecl(int,int)>cout << typeid(function1).name() << endl;cout << typeid(function2).name() << endl;cout << typeid(function3).name() << endl;cout << typeid(function4).name() << endl;//也可以进行相互转化function1 = function2;
}

最终,无论最开始他们是什么类型,到了最后都变成了类似于function<int(int,int)>的类型,也就可以进行相同类型之间的任何操作

包装器的应用场景 

通过包装器,我们可以轻轻松松实现函数回调

int main()
{//实现一个计算器map<string, function<int(int, int)>> calculator ={{"加法",[](int a,int b) {return a + b; }},{"减法",[](int a,int b) {return a - b; }},{"乘法",[](int a,int b) {return a * b; }},{"除法",[](int a,int b) {return a / b; }}};calculator["加法"](1, 20);
}

如果没有包装器,那么只能采用普通函数构建函数指针数组,既会产生大量命名冲突的风险,又会导致程序的简洁性大大降低。但是采用包装器,便把lambda表达式简洁易读的特点放到了最大。

包装器存在的问题

如果我们需要包装一个类中函数,我们应该怎么办?

 

class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};int main()
{//静态成员函数——没有this指针std::function<int(int, int)> funci = &Plus::plusi;//可以不写&cout << funci(1, 2) << endl;//非静态——有this指针std::function<double(Plus, double, double)> funcd = &Plus::plusd;//包装的时候必须有一个this的位置(必须写&)cout << funcd(Plus(), 1.1, 2.2) << endl;//通过匿名对象调用成员函数return 0;
}

如果是类中的静态函数,则和调用普通函数相同;而如果是类中非静态函数,则必须要在参数列表中加上this指针的类型,并且赋值时只能赋值为函数指针,调用时也必须传入this指针

 但是,往往我们使用的时候,并不喜欢每次都写this指针,那有没有办法省略掉this指针呢?

当然,这便是新的特性:bind


bind

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。 

bind的定义

继续看例子吧

也就是说,bind实际上是拷贝了其他函数或包装器,来定义一个新的包装器,在bind的第一个参数中,我们输入被绑定的名称,在后面依次输入参数的顺序,一直到参数的个数与被绑定的函数的参数个数相同。

这个参数个数我们可以进行一些小调整

强制参数

int normal_plus(int num1, int num2)
{return num1 + num2;
}int main()
{//第一个参数强制传入10//包装器模板参数中只需要传入一个参数//而参数的顺序为剩下参数的顺序function<int(int)> fplus = bind(normal_plus, 10, placeholders::_1);//调用时只需要传入一个参数fplus(2);//10+2
}

交换参数的顺序

int normal_plus(int num1, int num2)
{return num1 + num2;
}int main()
{//交换参数的顺序function<int(int, int)> fplus = bind(normal_plus, placeholders::_2,//第一个参数传入num2placeholders::_1//第二个参数传入num1);//传入的参数顺序为绑定的顺序fplus(10, 3);//3+10;
}

而通过这种方法,我们便可以写死this指针,不需要每次都传入this了


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/204943.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

时间序列预测实战(二十五)PyTorch实现Seq2Seq进行多元和单元预测(附代码+数据集+完整解析)

一、本文介绍 本文给大家带来的时间序列模型是Seq2Seq&#xff0c;这个概念相信大家都不陌生了&#xff0c;网上的讲解已经满天飞了&#xff0c;但是本文给大家带来的是我在Seq2Seq思想上开发的一个模型和新的架构&#xff0c;架构前面的文章已经说过很多次了&#xff0c;其是…

MySQL数据库,函数与分组

单行函数&#xff1a; 操作数据对象 接受参数返回一个结果 只对一行进行变换 每行返回一个结果 可以嵌套 参数也可以是一列或一个值 数值函数 基本函数&#xff1a; 注&#xff1a;ROUND(x,y)函数的y是负数时&#xff0c;即往高位进行四舍五入&#xff0c;如-3就是按百位…

出生率持续下降,而低代码,成了!

低代码这个概念在IT界应该是火了很久&#xff0c;在十年前就有低代码的概念。 在最初的时候&#xff0c;我们都是用高级语言或者脚本来开发页面或者应用&#xff0c;比如Java、C&#xff0c;前端会使用Vue、React等等。但是我们发现经常写的功能或者页面都是重复的&#xff0c;…

Python实现内网穿透和端口转发代理

对于大型的爬虫项目&#xff0c;肯定需要有良好的反爬机制&#xff0c;还应该配合代理iP使用&#xff0c;只有这两种结合才能让你的爬虫更高效准确。今天我就借用我一点空闲时间&#xff0c;来谈一谈有关python爬虫网络方面的解决方案&#xff0c;这里提供内网穿透和端口转发两…

印刷企业实施数字工厂管理系统有几个步骤

随着科技的飞速发展&#xff0c;许多传统行业正面临着前所未有的挑战。印刷行业也不例外&#xff0c;尤其在面对生产效率低下&#xff0c;资源浪费严重等问题时&#xff0c;数字工厂管理系统的实施成为了必然选择。那么&#xff0c;印刷企业实施数字工厂管理系统具体有几个步骤…

echarts 柱状图 定时自动轮播(非提示框轮播)

看了很多文档都是实现提示框轮播的&#xff0c;而我要实现的功能是&#xff1a;柱状图有多条数据时&#xff0c;轮播展示其中几条&#xff0c;比如我有100条数据&#xff0c;不能全部展示&#xff0c;设置轮播5条或者10条&#xff0c;依次显示数据&#xff0c;并形成闭环。 &a…

zabbix配置snmp trap--使用snmptrapd和Bash接收器--图文教程

1.前言 我的zabbix的版本是5.0版本&#xff0c;5.0的官方文档没有使用bash接收器的示例&#xff0c;6.0的官方文档有使用bash接收器的示例&#xff0c;但是&#xff0c;下载文件的链接失效&#xff1f;&#xff01; 这里讲解zabbix-server端配置和zabbix web端配置 2.zabbix-…

11、pytest断言预期异常

官方用例 # content of test_exception_zero.py import pytestdef test_zero_division():with pytest.raises(ZeroDivisionError):1/0# content of test_exception_runtimeerror.py import pytestdef test_recursion_depth():with pytest.raises(RuntimeError) as excinfo:def…

ncnn模型部署——使用VS2019把项目打包成DLL文件

一、项目打包成DLL文件 1.创建动态链接库DLL项目 创建完成&#xff0c;项目中包含源文件dllmain.cpp, pch.cpp&#xff0c;头文件framework.h, pch.h 2.编写和配置DLL项目 &#xff08;1&#xff09;配置pch.h文件&#xff0c;在头文件pch.h中定义宏&#xff0c;宏的作用的是…

【Unity动画】Unity 2D动画创建流程

本文以2D为案例&#xff0c;讲解Unity 播放动画的流程 准备和导入2D动画资源 外部导入序列帧生成的 Unity内部制作的 外部导入的3D动画 2.创建动画过程 打开时间轴Ctrl6 选中场景中的一个未来需要播放动画的物体 回到时间轴点击Create一个新动画片段 拖动2D动画资源放入…

什么是SPA(Single Page Application)?它的优点和缺点是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

IT外包对中小企业的独特优势

在竞争激烈的商业环境中&#xff0c;企业的发展稍有缓慢&#xff0c;就很有可能被竞争对手快速赶超、趁机抢占市场。一些中小企业为了更好地应对市场变化和提高自身竞争力&#xff0c;越来越多地转向了IT外包服务。相较于大型企业&#xff0c;中小企业在选择IT外包时能够获得一…

数据结构实验任务七:基于广度优先搜索的六度空间理论验证

问题描述 “六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论 可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个&#xff0c;也就是 说&#xff0c;最多通过五个人你就能够认识任何一个陌生人。”假如给你一个社交网络图&#xf…

Tap虚拟网卡

1 概述 Tap设备通常用于虚拟化场景下&#xff0c;其驱动代码位于drivers/net/tun.c&#xff0c;tap与tun复用大部分代码&#xff0c; 注&#xff1a;drivers/net/tap.c并不是tap设备的代码&#xff0c;而是macvtap和ipvtap&#xff1b; 下文中&#xff0c;我们统一称tap&#…

四、分代垃圾回收机制及垃圾回收算法

学习垃圾回收的意义 Java 与 C等语言最大的技术区别&#xff1a;自动化的垃圾回收机制&#xff08;GC&#xff09; 为什么要了解 GC 和内存分配策略 1、面试需要 2、GC 对应用的性能是有影响的&#xff1b; 3、写代码有好处 栈&#xff1a;栈中的生命周期是跟随线程&…

Python 日志(略讲)

日志操作 日志输出&#xff1a; # 输出日志信息 logging.debug("调试级别日志") logging.info("信息级别日志") logging.warning("警告级别日志") logging.error("错误级别日志") logging.critical("严重级别日志")级别设置…

Java程序员,你掌握了多线程吗?(文末送书)

目录 01、多线程对于Java的意义02、为什么Java工程师必须掌握多线程03、Java多线程使用方式04、如何学好Java多线程送书规则 摘要&#xff1a;互联网的每一个角落&#xff0c;无论是大型电商平台的秒杀活动&#xff0c;社交平台的实时消息推送&#xff0c;还是在线视频平台的流…

unity 2d 入门 飞翔小鸟 下坠功能且碰到地面要停止 刚体 胶囊碰撞器 (四)

1、实现对象要受重力 在对应的图层添加刚体 改成持续 2、设置胶囊碰撞器并设置水平方向 3、地面添加盒状碰撞器 运行则能看到小鸟下坠并落到地面上

Windows本地如何添加域名映射?(修改hosts文件)

1. DNS(域名系统) Domain Name System(域名系统)&#xff1a;为了加快定位IP地址的速度, 将域名映射进行层层缓存的系统. 目的&#xff1a;互联网通过IP&#xff08;10.223.146.45&#xff09;定位浏览器建立连接&#xff0c;但是我们不易区别IP&#xff0c;为了方便用户辨识I…

柏睿网络分析:为什么微模块化机房越来越受欢迎?

与传统机房相比&#xff0c;微模块化机房的建设周期更短&#xff0c;扩展性更强&#xff0c;能耗更低&#xff0c;运维难度也相对较低。因此&#xff0c;微模块化机房是一种高效、灵活、节能的机房解决方案&#xff0c;适用于各种规模的数据中心。 一体化分布式部署&#xff1a…