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,一经查实,立即删除!

相关文章

windows 安装两个mysql

参考链接一 参考链接二 安装第二个mysql 端口号改为3307进入 bin目录管理员身份运行cmd mysqld --defaults-fileC:\\soft\\1mysql-5.7.33-winx64\\my.ini --initialize --console 初始化 data 目录修改密码 修改 my.ini 文件添加 skip-grant-tables 见下启动mysql mysqld -…

了解linux计划任务

本章主要介绍如何创建计划任务 使用 at 创建计划任务 使用 crontab 创建计划任务 有时需要在某个指定的时间执行一个操作&#xff0c;此时就要使用计划任务了。计划任务有两种&#xff1a; 一个是at计划任务&#xff0c;另一个是 crontab计划任务。 下面我们分别来看这两种计…

Python 几个简单的案例

1.1 作业讲解 输入一个数字&#xff0c;求解此数字的所有因子之和&#xff01; 如 6 的因子有 1&#xff0c;2&#xff0c;3&#xff0c;6&#xff0c;所有因子相加 123612 numint( input("请输入一个数字:")) #两个核心逻辑&#xff1a; 因子&#xff0c;求和 s0 fo…

c++实验多态程序设计

运行程序&#xff0c;分析结果。 #include <iostream> using namespace std; class B { public: virtual void f1(double x) { cout<<"B::f1(double)"<<x<<endl; } void f2(double x) { cout<<"B::f2(double)"<<…

IDEA版SSM入门到实战(Maven+MyBatis+Spring+SpringMVC) -Mybatis中参数传递问题

第一章 Mybatis中参数传递问题 1.1 单个普通参数 可以任意使用&#xff1a;参数数据类型、参数名称不用考虑 1.2 多个普通参数 Mybatis底层封装Map结构&#xff0c;封装key为param1、param2…【支持&#xff1a;arg0、arg1、…】 1.3 命名参数 语法&#xff1a; Param(val…

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

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

MySQL数据库,函数与分组

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

Python-函数详解(局部、全局变量)

函数:一段可以被重复利用的代码 函数定义&#xff1a; def 函数名 (参数) 函数体 return 返回值(可有可无) def 函数名()#函数参数#函数体return #返回值&#xff08;可有可无&#xff09; 函数的调用&#xff1a;先定义&#xff0c;后调用 警告和错误&#xff1a; …

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

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

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

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

分治法解决众数重数

问题 D: 22-数组-2-众数问题 [命题人 : 外部导入] 时间限制 : 1.000 sec 内存限制 : 128 MB 题目描述 问题描述&#xff1a; 给定含有n个元素的多重集合S&#xff0c;每个元素在S中出现的次数称为该元素的重数&#xff0c;其中重数最大的元素称为众数。 例如&#xff0c;S{1&a…

基于多波束和时间调制平均的相位量化栅瓣抑制方法极其性能极限

概要 为了抑制相位量化所导致的量化栅瓣,在波束合成中引入随机虚拟相移(RVPS,Random Virtual Phase Shift),采用多波束平均(MBS,Multi-beam Average)或时间调制平均(TMA,Time-Modulated Average)方法实现了对量化栅瓣的抑制,仿真验证了方法的有效性及限制,并进一…

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

随着科技的飞速发展&#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…

高效的单行python脚本

#-- coding: utf-8 -- “”" Created on Wed Dec 6 13:42:00 2023 author: czliu “”" 1. 平方列表推导 #使用列表推导法计算从 1 到 10 的数字平方 squares [x**2 for x in range(1, 11)] print(squares)2.求偶数 #可以使用列表推导式从列表中筛选偶数。还可以…

【Docker】 Docker attach 命令卡死的解决方案

在启动较早期的容器时&#xff0c;出现了使用 docker attach container_name 命令后卡死的问题。本文介绍了如何解决该问题。 首先&#xff0c;在使用 docker attach container_name 命令前需要先保证容器已经开启。此时&#xff0c;输入 docker attach container_name 命令后…

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;宏的作用的是…

【PyTorch】模型

文章目录 1. 模型的创建1.1. 创建方法1.1.1. 通过使用模型组件1.1.2. 通过继承nn.Module类 1.2. 将模型转移到GPU 2. 模型参数初始化3. 模型的保存与加载3.1. 只保存参数3.2. 保存模型和参数 1. 模型的创建 1.1. 创建方法 1.1.1. 通过使用模型组件 可以直接使用模型组件快速…