【C++11】包装器和bind

news2024/11/17 19:32:48

文章目录

  • 一. 为什么要有包装器?
  • 二. 什么是包装器?
  • 三. 包装器的使用
  • 四. bind 函数模板
    • 1. 为什么要有 bind ?
    • 2. 什么是 bind ?
    • 3. bind 的使用场景

一. 为什么要有包装器?

function 包装器,也叫作适配器。C++ 中的 function 本质上是一个类模板,也是一个包装器。接下来我们来看看,C++ 为什么引入 function 呢?

在 C++ 中,可调用对象有以下三种:函数名/函数指针、仿函数对象、lambda 表达式

// 加法普通函数
int NormalPlus(int num1, int num2)
{
	return num1 + num2;
}

// 加法仿函数
class FunctorPlus
{
public:
	int operator()(int num1, int num2)
	{
		return num1 + num2;
	}
};

int main()
{
	// 加法 lambda 表达式
	auto LambdaPlus = [](int num1, int num2) {return num1 + num2; };

	// 1、调用普通函数
	NormalPlus(10, 20);
	// 2、调用仿函数对象
	FunctorPlus obj;
	obj(10, 20);
	// 3、调用 lambda 表达式
	LambdaPlus(10, 20);
}

可以看到,它们的调用方式可以不能说相似,只能说是一模一样,且它们函数体中执行的内容都是完全相同的;但是它们彼此之间的类型不同,不能进行相互赋值等操作。
在这里插入图片描述

上面是三种不同的可调用对象,那么它们的类型不同还可以理解;但是在 lambda 表达式中,就算功能相似的 lambda 表达式,它们直接的类型也互不相同:
在这里插入图片描述

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

包装器的诞生就是为了解决这个问题,通过包装器,可以让功能相似的可调用对象(函数名/函数指针、仿函数对象和 lambda 表达式)的类型统一,使其可以相互联系,相互转化。

二. 什么是包装器?

包装器是个类模板,它的定义在头文件 functional 中
在这里插入图片描述

在这里插入图片描述

下面看看具体的示例:
在这里插入图片描述

也就是说,在 function 的模板列表中,第一个参数是返回值的类型,随后在括号里依次传入形参的类型;而后在赋值的时候,只需要让其等于已经定义过的可调用对象,这样就算包装完成了,最后我们可以使用这个包装好的对象来代替之前的可调用对象,去执行它们的功能。

三. 包装器的使用

还是刚刚的例子,我们分别包函数名/函数指针,仿函数对象,lambda 表达式,看看包装完之后它们各自的类型是什么

// 加法普通函数
int NormalPlus(int num1, int num2)
{
	return num1 + num2;
}

// 加法仿函数
class FunctorPlus
{
public:
	int operator()(int num1, int num2)
	{
		return num1 + num2;
	}
};

int main()
{
	// 加法 lambda 表达式
	auto LambdaPlus = [](int num1, int num2)->int{return num1 + num2; };

	// 使用包装器包装函数名
	function<int(int, int)> function1 = NormalPlus;

	// 使用包装器包装函数地址
	function<int(int, int)> function2 = &NormalPlus;

	// 使用包装器包装仿函数对象(这里的 FunctorPlus() 是一个匿名的仿函数对象)
	function<int(int, int)> function3 = FunctorPlus(); 

	// 使用包装器包装 lambda 表达式
	function<int(int, int)> function4 = LambdaPlus;

	// 其类型都为 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 = function3;
	function3 = function4;

	return 0;
}

------运行结果-------
class std::function<int __cdecl(int,int)>
class std::function<int __cdecl(int,int)>
class std::function<int __cdecl(int,int)>
class std::function<int __cdecl(int,int)>

可以发现,这几个可调用对象无论最开始它们是什么类型,最后都被包装器给包装成了 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; }}
	};

	cout << calculator["加法"](10, 20) << endl;
	cout << calculator["减法"](10, 20) << endl;
}

------运行结果-------
30
-10

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

四. bind 函数模板

1. 为什么要有 bind ?

前面一直说的是普通函数,别忘了还有类中的成员函数,那包装器如何包装和调用成员函数呢?

class A
{
public:
	// 普通成员函数
	int Plus(int num1, int num2)
	{
		return num1 + num2;
	}
	// 静态成员函数
	static int PlusStatic(int num1, int num2)
	{
		return num1 + num2;
	}
};

int main()
{
	// 静态成员函数(没有this指针,其实和普通函数没什么区别,函数名和函数地址相同)
	function<int(int, int)> funcStatic1 = A::PlusStatic;
	function<int(int, int)> funcStatic2 = &A::PlusStatic;

	// 非静态成员函数(有this指针,this其实就是该类的对象,位于在参数列表中第一个参数位置)
	// 包装非静态成员函数时,不能用函数名,必须使用函数地址;对非静态成员函数而言,函数名和函数地址不同)
	function<int(A, int, int)> func1 = &A::Plus;

	// 调用包装器对象
	cout << funcStatic1(10, 20) << endl;
	cout << funcStatic2(10, 20) << endl;
	cout << func1(A(), 10, 20) << endl; // 非静态成员函数需要传入一个对象,然后通过这个对象去调用

	return 0;
}

------运行结果-------
30
30
30

总结一下关于成员函数的包装:

  • 类的静态成员函数和普通函数性质一样,它们的函数名和函数地址等价;但是非静态成员函数的地址则必须使用取地址运算符“&”。
  • 包装非静态成员函数时需要增加一个参数(this 指针,this 其实就是一个实例化的类对象),因为非静态成员函数需要用对象去调用,且非静态成员函数的第一个参数是隐藏 this 指针,因此在包装时需要指明第一个形参的类型为类的名称。
  • 静态成员函数因为没有 this 指针,所以它本质上其实和普通函数一样。
  • 调用包装好的非静态成员函数时,注意在参数列表中第一个参数位置传入该类的一个实例化对象,通常都是传的都是匿名对象。

为了让包装好的非静态成员函数使用起来更简单(不想每次使用时都要传入一个实例化对象), C++11 增加了新特性:bind 模板

2. 什么是 bind ?

std::bind 函数定义在头文件 functional 中,是一个函数模板,它也有点像上面的包装器(适配器),接受一个可调用对象(函数/函数名、仿函数对象、lambda 表达式),然后生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收 N 个参数的可调用对象 Func,通过绑定一些参数,返回一个接收 M 个(通常 M <= N)参数的新函数。另外,使用 std::bind 模板还可以修改参数的传参顺序。

具体说的话,bind 可以去给可调用对象(通常是静态成员函数)参数列表中的参数指定缺省值,或者更改形参的接收顺序,然后生成一个新的可调用对象来“适应”原对象的参数列表。

下面是 bind 的定义:
在这里插入图片描述

具体示例:
在这里插入图片描述

在 bind 的第一个参数中,我们输入被绑定的可调用对象的名称,后面再依次输入传参进来的参数的顺序。

3. bind 的使用场景

在绑定时,参数列表中参数的个数和顺序我们可以进行一些小调整:

作用一:给参数设定缺省值
在这里插入图片描述

作用二:调整传参顺序

int normal_plus(int num1, int num2)
{
	return num1 + num2;
}

int main()
{
	// 交换参数的顺序
	function<int(int, int)> fplus = bind
	(	
		normal_plus,
		placeholders::_2,//第一个参数传入 num2
		placeholders::_1 //第二个参数传入 num1
	);

	// 传入参数顺序为绑定的顺序
	fplus(10, 3); //3+10;
}

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

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

相关文章

js之原型链

在JavaScript中&#xff0c;原型链是一种用于实现继承和属性查找的机制。每个对象都有一个内部属性[[Prototype]]&#xff0c;这个属性指向创建该对象时使用的构造函数的“prototype"属性。对象的方法和属性定义在它的原型对象上。 1.原型&#xff08;Prototypes&#xf…

【Netty 源码】服务端启动流程源码分析 篇一

【Netty 源码】服务端启动流程源码分析 篇一 1.原生Java NIO服务端创建流程 使用Java NIO创建服务端时&#xff0c;通常我们需要先创建Channel&#xff0c;Selector两个对象&#xff0c;然后将Channel绑定端口并注册到Selector上&#xff0c;最后对事件轮询监听 //第一步&…

ptmalloc、tcmalloc与jemalloc对比分析

文章目录 一、内存管理二、ptmalloc系统角度看ptmalloc内存管理用户角度看ptmalloc内存管理线程中内存管理从工作原理来看从作用来看Chunk说明问题 三、tcmalloc系统角度看tcmalloc内存管理用户角度看tcmalloc内存管理tcmalloc的优势 四、jemalloc系统角度看jemalloc内存管理用…

【Web - 框架 - Vue】随笔 - Vue的简单使用(01) - 快速上手

【Web - 框架 - Vue】随笔 - Vue的简单使用(01) - 快速上手 Vue模板代码 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>模板</title> </head> <body> <div></di…

js 获取浏览器相关的宽高尺寸

window 屏幕 屏幕分辨率的高&#xff1a; window.screen.height 屏幕分辨率的宽&#xff1a; window.screen.width 屏幕可用工作区高度&#xff1a; window.screen.availHeight 屏幕可用工作区宽度&#xff1a; window.screen.availWidth document 网页 网页可见区域宽&#xf…

基于网格搜索的随机森林回归算法Python实现

摘要 随机森林回归算法的应用领域很广&#xff0c;可用于市场销售预测、客户细分、商品推荐等领域&#xff0c;也可应用于气候模型预测、土地利用分析、水资源管理等环境领域问题。其算法的Python实现涉及到多参数调优问题&#xff0c;本文使用了网格搜索法&#xff0c;以MSE作…

Springer旗下SCI,16天见刊!稳定检索13年,质量稳定

毕业推荐 SCIE&#xff1a; • 计算机类&#xff0c;6.5-7.0&#xff0c;JCR1区&#xff0c;中科院2区 • 2个月19天录用&#xff0c;6天见刊&#xff0c;36天检索 SCI&EI&#xff08;CCF-C类&#xff09; • 算法类&#xff0c;2.0-3.0&#xff0c;JCR3区&#xff0c…

笔记76:32位/64位操作系统的区别

64位系统和32位系统的区别: 操作系统只是硬件和应用软件中间的一个平台32位操作系统针对的32位的CPU设计64位操作系统针对的64位的CPU设计我们的CPU从原来的8位&#xff0c;16位&#xff0c;到现在的32位和64位&#xff1b;CPU处理计算的时候“数据”和“指令”是不同对待的 &…

解锁网络数据:入门级IP代理使用教程

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

Spring Security认证授权流程详解

认证的工作原理 过滤链 Spring Security框架的作用就是安全访问控制即对所有进入系统的请求进行拦截, 校验每个请求是否能够访问到它所期望的资源 通过Filter或AOP等技术可以实现安全访问控制功能,而Spring Security对Web资源的保护是靠Filter实现的,Spring Security有一个过…

在 .NET 项目中复制资源文件夹到生成目录

本文主要介绍在使用 Visual Studio 进行调试和发布时&#xff0c;如何在 .NET 项目中复制资源文件夹到生成目录。 1. 背景 在开发 .NET 项目的过程中&#xff0c;我们有时会遇到需要在 debug 、 release 或是发布时将资源文件夹复制到生成目录的需求。这些资源可能包括图片、配…

Java旋转矩阵

题目&#xff1a; 给你一幅由 N N 矩阵表示的图像&#xff0c;其中每个像素的大小为 4 字节。请你设计一种算法&#xff0c;将图像旋转 90 度。 不占用额外内存空间能否做到&#xff1f; 示例 1: 给定 matrix [ [1,2,3], [4,5,6], [7,8,9] ], 原地旋转输入矩阵&…

运筹从业者也需要的因果推断入门:基础概念解析和体系化方法理解

文章目录 1 引言2 相关关系 VS 因果关系2.1 相关关系2.2 因果关系2.3 相关关系不等于因果关系 3 因果推断方法3.1 方法体系3.2 方法理解 4 运筹从业者也需要因果推断4.1 问题描述4.2 算法方案4.3 算法验证 5 总结6 相关阅读 1 引言 已经3月初了&#xff0c;原计划的因果推断学…

抖音在线点赞任务发布接单运营平台PHP网站源码多个支付通道+分级会员制度,附带系统搭建教程

介绍 1、三级代理裂变&#xff0c;静态返佣/动态返佣均可设置。&#xff08;烧伤制度&#xff09;。 2、邀请二维码接入防红跳转。 3、自动机器人做任务&#xff0c;任务时间可设置&#xff0c;机器人价格时间可设置。 4、后台可设置注册即送X天机器人。 5、不同级别会员使…

浅谈JUC的理解(含JUC知识体系图)

浅谈JUC的理解 一、前言感悟二、并发知识三、一年前回答四、补充体系回答五、补充层次回答六、碎碎念 本文除了说技术&#xff0c;更多的是在一个两年多开发经验的程序员视角下&#xff0c;记录下自己探索到的世界。 如有不妥之处&#xff0c;还请指正。共勉。 一、前言感悟 当…

上海雷卯湿敏元器件存储及使用规范

湿敏等级是指材料或产品对湿度变化的敏感程度。它用于评估材料或产品在湿度变化条件下的稳定性和可靠性。 湿敏等级通常通过数字表示&#xff08;如MSL- Moisture Sensitivity Level&#xff09;&#xff0c;从1到6级不等&#xff0c;每个级别代表不同的湿敏程度。较低的级别表…

基于网络爬虫的购物平台价格监测系统的设计与实现

通过对网络爬虫的购物平台价格监测系统的业务流程进行梳理可知&#xff0c;网络爬虫的购物平台价格监测系统主要由前台买家模块、后台卖家模块以及管理员模块构成。前台功能包含登录功能、注册功能、系统首页功能、唯品会商品详情浏览、唯品会商品收藏、唯品会商品点赞、唯品会…

9. 内核、文件系统加载工具

内核、文件系统加载工具 内核、文件系统加载工具是嵌入式开发必备的工具 1. 烧写BootLoader 1.1 通过超级终端方式 烧写 Bootloader 可以使用超级终端的“传送” |“发送文件”命令进入发送文件对话框&#xff0c;使用 Xmodem 协议和 Kermit 协议发送 Bootloader 的各个文件…

DenseNet笔记

&#x1f4d2;from ©实现pytorch实现DenseNet&#xff08;CNN经典网络模型详解&#xff09; - 知乎 (zhihu.com) 是什么之 DenseBlock 读图&#xff1a; x0是inputH1的输入是x0 (input)H2的输入是x0和x1 (x1是H1的输出) Summary&#xff1a; 传统卷积网&#xff0c;网…

角蜥优化算法 (Horned Lizard Optimization Algorithm ,HLOA)求解无人机路径优化

一、无人机路径规划模型介绍 无人机三维路径规划是指在三维空间中为无人机规划一条合理的飞行路径,使其能够安全、高效地完成任务。路径规划是无人机自主飞行的关键技术之一,它可以通过算法和模型来确定无人机的航迹,以避开障碍物、优化飞行时间和节省能量消耗。 二、算法介…