【C++11】包装器

news2024/11/25 15:23:34

目录

1.function包装器

1.1什么是函数包装器(function)? 

1.2为啥使用函数包装器(function)?

2.bind包装器 

 2.1绑定普通函数和调整传参顺序

2.2绑定类成员函数


1.function包装器

头文件#include<functional>

1.1什么是函数包装器(function)? 

function:是一个通用多态函数包装器是一个类模板可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数函数对象函数指针成员函数(静态和非静态)并允许保存和延迟它们的执行。

std::function可以取代函数指针的作用,因为它可以延迟函数的执行,特别适合作为回调函数使用。

​
template <class T> function;     // undefined
template <class Ret, class... Args> 
class function<Ret(Args...)>;

​
  • Ret被包装的可调用对象的返回值类型。
  • Args...被包装的可调用对象的形参类型。

function:是一个通用多态函数包装器是一个类模板可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数函数对象函数指针成员函数(静态和非静态)并允许保存和延迟它们的执行。

std::function可以取代函数指针的作用,因为它可以延迟函数的执行,特别适合作为回调函数使用。

//function包装器
int Plus(int a, int b)
{
	return a + b;
}

class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}
};

int main()
{
	function<int(int, int)>funcPlus = Plus;
	function<int(Sub, int, int)> funcSub = &Sub::sub;
}

这个就是一个简单的函数包装器。

1.2为啥使用函数包装器(function)?

由于函数调用可以使用函数名、函数指针、函数对象或有名称的lambda表达式,可调用类型太丰富导致模板的效率极低。包装器用于解决效率低的问题。

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;   //count:1 count:0025C140  5.555
	// 函数对象
	cout << useF(Functor(), 11.11) << endl;   //count:1 count: 0025C144 3.70333
	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;  //count : 1 count: 0025C148 2.7775
	return 0;
}

在此源码(这份源码是我借用一位师兄的) 中发现useF实例化了三份并且每一个count地址都是不同的。

而C++11新出了函数包装器就可以对可调用对象进行包装,包括函数指针(函数名)、仿函数(函数对象)、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:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	//1、包装函数指针(函数名)
	function<int(int, int)> func1 = f;
	cout << func1(1, 2) << endl;

	//2、包装仿函数(函数对象)
	function<int(int, int)> func2 = Functor();
	cout << func2(1, 2) << endl;

	//3、包装lambda表达式
	function<int(int, int)> func3 = [](int a, int b){return a + b; };
	cout << func3(1, 2) << endl;

	//4、类的静态成员函数
	//function<int(int, int)> func4 = Plus::plusi;
	function<int(int, int)> func4 = &Plus::plusi; //&可省略
	cout << func4(1, 2) << endl;

	//5、类的非静态成员函数
	function<double(Plus, double, double)> func5 = &Plus::plusd; //&不可省略
	cout << func5(Plus(), 1.1, 2.2) << endl;
	return 0;
}
  • 取静态成员函数的地址可以不用取地址运算符“&”,但取非静态成员函数的地址必须使用取地址运算符“&”。
  • 包装非静态的成员函数时需要注意,非静态成员函数的第一个参数是隐藏this指针,因此在包装时需要指明第一个形参的类型为类的类型。

就像上面的源码一样,我们在包装f()和fector函数是只需要传2个参数,但是在包装plusd()时就需要传递3个参数。

假设我现在利用包装器建立字符串和对应函数的映射关系,并放到map容器里头,此时就会出现问题了:成员函数会有三个参数,而我map容器里的value位置仅允许传两个参数,导致参数无法匹配:所以此时C++大神们就想出来了bind捆绑器。

2.bind包装器 

  1. bind是一个标准库函数,定义在functional头文件中。可以将bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成新的可调用对象来适应原对象的参数列表。
  2. bind将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function保存。bind绑定完成后,返回一个函数对象,它内部保存了原可调用对象的拷贝,具有operator(),返回值类型被自动推导为原可调用对象的返回值类型。调用时,这个函数对象将把之前存储的参数转发给原可调用对象完成调用。

绑定函数参数:

template <class Fn, class... Args>
  /* unspecified */ bind (Fn&& fn, Args&&... args);
带返回类型 (2)	
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中的参数。
  • arg_list中的参数可能包含形如_n的占位符,如_1,_2,代表了newCallable中相应位置的参数等等。

 2.1绑定普通函数和调整传参顺序

第一个参数为要绑定的可调用对象,后跟参数列表(参数列表同可调用对象的参数列表匹配)。参数列表中可包含名字形如  _n (n为整数)的占位符,n表示生成的可调用对象中参数的位置。placeholders::_1和placeholders::_2,表示后续调用新生成的可调用对象时,传入的第一个参数传给placeholders::_1,传入的第二个参数传给placeholders::_2。占位符位于std::placeholders命名空间。

//bind捆绑器
int Add(int a, int b)
{
	return a + b;
}

int main()
{
	std::function<int(int)>func_Add2 = std::bind(Add, std::placeholders::_1, 5);
	//int result = func_add1(18); //等于18+5
	cout << "func_Add2(18+5):" << func_Add2(18) << endl;

	//对参数重排序
	using namespace std::placeholders;
	auto func_Add3 = std::bind(Add, _2, _1); //参数位置对换了
	cout << "func_Add3(18,5):" << func_Add3(18,5) << endl;
}

其实在第一个绑定中std::bind(Add, std::placeholders::_1, 5),我们把Plus函数的第二个参数固定绑定为5了,第一个定绑定参数没变,此时调用绑定后新生成的可调用对象时就只需要传入一个参数,它会将该值与5相加后的结果进行返回。

而第二个例子是把参数列表中的前两个参数的顺序给改变了,但是还是这两个数相加。这个不显,如果是两数相减就明显了。

2.2绑定类成员函数

bind绑定类成员函数时:

  • 第一个参数表示对象的成员函数的指针。
  • 第二个参数表示对象的地址。
struct Func {
	void Print_sum(int a, int b)
	{
		std::cout << a - b << '\n';
	}
	int data = 10;
};
int main()
{
	Func tmp;
	auto f = std::bind(&Func::Print_sum, &tmp, 12, std::placeholders::_1);
	f(25); // -13
}

注意:必须显示的指定&Func::Print_sum,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Func::Print_sum前添加&取地址符;使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &tmp.

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

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

相关文章

2022年终总结

不知不觉就到了年末&#xff0c;感叹时间过的真快。 我自己坚持写了七年多的博客&#xff0c;但这其实是我第一次去写年终总结。也不知道怎么写&#xff0c;就简单聊聊。 写博客的初衷就是个人收获&#xff0c;学习的记录&#xff0c;分享出来如果能帮到别人那就更好了。毕竟…

(一)LTspice简介2

文章目录前言一、LTspice的仿真过程二、spice的模型三、LTspice的工具栏和快捷键四、LTspice中的数量级前言 上一节我们学习了LTspice的安装&#xff0c;很简单&#xff0c;无脑安装 &#xff08;一&#xff09;LTspice安装 这一节我们继续学习LTspice的简介&#xff0c;主要包…

题:A-B 数对(二分)

A-B 数对 - 洛谷 题目背景 出题是一件痛苦的事情&#xff01; 相同的题目看多了也会有审美疲劳&#xff0c;于是我舍弃了大家所熟悉的 AB Problem&#xff0c;改用 A-B 了哈哈&#xff01; 题目描述 给出一串正整数数列以及一个正整数 CC&#xff0c;要求计算出所有满足 A…

通过Wireshark分析Apache服务器的SSL协议工作过程

文章目录一、实验环境二、为Apache服务器启用SSL1.获取SSL证书2.修改httpd.conf配置文件3.修改httpd-ssl.conf配置文件4.启动Apache服务三、SSL/TLS工作过程分析一、实验环境 操作系统&#xff1a;macOS Ventura 13.0.1 Apache&#xff1a;Apache/2.4.54 (Unix)&#xff0c;此…

喜提JDK的BUG一枚、多线程的情况下请谨慎使用这个类的stream遍历。

前段时间在 RocketMQ 的 ISSUE 里面冲浪的时候&#xff0c;看到一个 pr&#xff0c;虽说是在 RocketMQ 的地盘上发现的&#xff0c;但是这个玩意吧&#xff0c;其实和 RocketMQ 没有任何关系。 纯纯的就是 JDK 的一个 BUG。 我先问你一个问题&#xff1a;LinkedBlockingQueue…

vue3 antd table表格的样式修改(二)利用rowClassName给table添加行样式

vue3 antd项目实战——修改ant design vue组件中table表格的默认样式&#xff08;二&#xff09;知识调用场景复现修改table表格的行样式一、rowClassName添加行样式二、表格的不可控操作写在最后知识调用 文章中可能会用到的知识链接vue3ant design vuets实战【ant-design-vu…

Autosar MCAL-SPI配置及使用

文章目录前言SPI协议基础Autosar SPI专有名词SpiDriverSpiChannelSpiChannelIdSpiChannelTypeSpiDataWidthSpiDefaultDataSpiEbMaxLengthSpiIbNBuffersSpiTransferStartSpiExternalDeviceSpiBaudrateSpiAutoCalcBaudParamsSpiCsIdentifierSpiCsPolaritySpiCsSelectionSpiDataSh…

前端工程师可以分成 4 种,你属于哪一种?

在这篇文章中&#xff0c;探讨四种常见的前端工程&#xff0c;1&#xff09;产品工程师&#xff0c;2&#xff09;UI 基建工程师&#xff0c;3&#xff09;设计师&#xff0c;4&#xff09;工具基建工程师&#xff0c;你属于哪一种&#xff1f; 产品工程师 产品工程师负责公司…

6.s081 学习实验记录(二)xv6 and unix utilities

文章目录一、boot xv6二、sleep三、pingpong四、primes五、find六、xargs该实验主要用来熟悉xv6以及其系统调用 一、boot xv6 实验目的&#xff1a; 启动xv6系统&#xff0c;并使用提供的命令ls&#xff0c;列出系统所有的文件ctrl p&#xff0c;打印当前运行的进程ctrl a…

Ubuntu22.04使用kubeadm安装k8s 1.26版本高可用集群

目录阿里云ACK集群的架构ACK实例的创建过程如下安装前的准备主机规划基线准备所有k8s master、worker节点安装kubeadmkubectlkubelet创建集群负载均衡器HAproxy安装keepalived 和haproxy配置haproxy配置keepalivedkubeadm部署第一台master节点Calico网络组件一键安装安装完成阿…

mPEG-Biotin,甲氧基-聚乙二醇-生物素科研实验用试剂

​​ 英文名称&#xff1a;mPEG-Biotin 中文名称&#xff1a;甲氧基-聚乙二醇-生物素 mPEG生物素可通过与链霉亲和素和抗生物素结合进行聚乙二醇化&#xff0c;具有高亲和力和特异性。生物素通过稳定的酰胺连接物与线性PEG结合。 提示&#xff1a;避免频繁的溶解和冻干&…

node.js+uni计算机毕设项目基于微信的同学会小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

GraphQL在Django中的使用

简介 特点 请求你所要的数据&#xff0c;不多不少获取多个资源&#xff0c;只用一个请求描述所有的可能&#xff0c;类型系统几乎所有语言支持 谁在用 文档 Graphene-PythonGraphQL | A query language for your API 背景 传统restful的接口定义类型多&#xff0c;试图简…

WordPress 添加百度主动推送代码,加速网站收录保护原创文章

WordPress是世界上使用人数最多开源程序之一&#xff0c;它的优点有很多&#xff0c;譬如&#xff0c;简单易用、样式丰富&#xff0c;模板众多&#xff0c;安全性能高&#xff0c;对搜索引擎友好&#xff0c;收录快、扩展性强&#xff0c;功能强大等等&#xff0c;其中我最喜欢…

JVM的类加载机制

一、类加载机制 类的加载指的是将类的.class文件中的二进制数据读入到内存中&#xff0c;将其放在运行时数据区的方法区内****&#xff0c;然后在堆区创建一个java.lang.Class对象&#xff0c;用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象&…

六、应用层(三)文件传输协议(FTP)

将某台计算机中的文件通过网络传送到可能相距很远的另一台计算机中&#xff0c;是一项基本的网络应用&#xff0c;即文件传送。 文件传送协议FTP&#xff08;File Transfer Protocal&#xff09;是因特网上使用得最广泛的文件传送协议。 FTP提供交互式的访问&#xff0c;允许…

欢祝圣诞——北斗星的爱!

2022年圣诞节到来啦&#xff0c;很高兴这次我们又能一起度过~ ★★给大家推荐两首我很喜欢的歌曲&#xff0c;第一首是标题同名歌曲&#xff1a;1.张杰的《北斗星的爱》&#xff1b;另一首是&#xff1a;2.张杰的《星星》&#xff0c;这两首歌真的很震撼很好听&#xff01; 我…

2022细胞生物学实验原理复习资料汇总

2022细胞生物学实验原理复习资料汇总1.2022年考试复习题及参考答案2.2021年考试复习题及参考答案3.2020年之前考试复习题汇总4.复习重点&#xff08;汇总&#xff09;5.排版好的PDF高清版 获取途径&#xff08;资源2021年版&#xff0c;链接2022年最新版本&#xff09;1.2022年…

Linux网络编程之epoll多路转接服务器

Linux网络编程之epoll多路转接服务器 一、epoll的基本概念 epoll是Linux下多路复用IO接口select/poll的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率&#xff0c;因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待…

基于自动模糊聚类的图像分割研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…