C++11|包装器

目录

引入 

一、function包装器

1.1包装器使用

1.2包装器解决类型复杂 

二、bind包装器


引入 

在我们学过的回调中,函数指针,仿函数,lambda都可以完成,但他们都有一个缺点,就是类型的推导复杂性,从而会导致某一问题,例如:

#include <iostream>
using namespace std;


// 定义一个仿函数
struct DivideByTwo {
    double operator()(double num) {
        return num / 2.0;
    }
};

// 定义一个函数
double divideByThree(double num) {
    return num / 3.0;
}


template <class F,class T>
T useF(F f, T x)
{
    return f(x);
}

int main() {


    // 直接使用仿函数
    cout << useF(DivideByTwo(), 10.0) << endl;

    // 直接使用函数指针
    cout << useF(divideByThree, 10.0) << endl;

    //lambda表达式
    cout << useF([](double d) {return d / 4; }, 10.0) << endl;

    return 0;
}

输出结果:

在上述示例中,由于类型的复杂,useF函数模板需要实例化三份,从而使得代码变得相对冗余,面对这种问题,function包装器可以带来一定的优化。那么接下来讲讲包装器的用法,然后来解决这个问题。

一、function包装器

1.1包装器使用

function包装器也叫适配器,是一个类模板。因为是一个适配器,所以可以适配出函数指针,仿函数,lambda的用法。

std::function包装器在头文件functional>

 类模板原型如下:

template <class Ret, class... Args>

class function<Ret(Args...)>;

模板参数说明: Ret: 被调用函数的返回类型

Args…:被调用函数的形参

#include <iostream>
using namespace std;
#include <functional>
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()
{
	// 函数名(函数指针),
	function<int(int, int)> func1 = f;//将函数指针传给包装器,就可以适配出具有相应函数指针的功能
	cout << func1(1, 2) << endl;//调用的就是函数f,被调用函数的返回值类型是int,形参是int,int

	// 函数对象
	function<int(int, int)> func2 = Functor(); //将仿函数对象传给包装器,就可以适配出仿函数
	cout << func2(1, 2) << endl;//调用的就是仿函数operator(),被调用仿函数的返回值类型是int,形参是int,int

	// lambda表达式
	function<int(int,int)> func3 = []( int a, int b){return a + b; };//将lambda传给包装器,就可以适配出lambda
	cout << func3(1, 2) << endl;//调用的就是lambda,而其底层是仿函数,即调用对应的operator(),被调用仿函数的返回值类型是int,形参是int,int

	// 类的成员函数,注意:成员函数取地址必须加&,由于是在类外,所以还得加上类域
	function<int(int, int)> func4 = &Plus::plusi;//将静态成员函数指针传给包装器,就可以适配出具有相应成员函数指针的功能
	cout << func4(1, 2) << endl;//调用的就是plusi函数

	function<double(Plus*, double, double)> func5 = &Plus::plusd;//由于是非静态成员函数,其多了一个this指针,所以得多带上一个参数
	//cout << func5(&Plus(), 1.1, 2.2) << endl;//由于plus()是一个匿名对象,属于右值,右值不能够取地址,所以不能这样写
	Plus ps;//得额外定义一个对象
	cout << func5(&ps, 1.1, 2.2) << endl;

	//还可以这种写法,相当于用对象去调用了这个成员函数,可以认为是编译器的特殊处理
	function<double(Plus, double, double)> func6 = &Plus::plusd;
	cout << func6(Plus(), 1.1, 2.2) << endl;
	return 0;
}

输出结果:

1.2包装器解决类型复杂 

回到最开始引入的问题, 使用包装器就可以解决,如下:

int main() {


    // 直接使用仿函数
    function<double(double)> func1 = DivideByTwo();
    cout << useF(func1, 10.0) << endl;

    // 直接使用函数指针
    function<double(double)> func2 = divideByThree;
    cout << useF(func2, 10.0) << endl;

    //lambda表达式
    function<double(double)> func3 = [](double d) {return d / 4; };
    cout << useF(func3, 10.0) << endl;
   

    return 0;
}

 输出结果:

function<double(double)> 作为参数传递给 useF 函数模板。那么,模板会根据这个特定的参数类型(即接受一个 double 类型参数并返回 double 类型值的可调用对象)进行一次实例化。无论以这种类型的function 调用 useF 函数多少次,只要参数类型不变,都只会进行这一次实例化。相比较传入不同类型实例化有效地减少了实例化的次数和复杂性

二、bind包装器

也是定义在functional文件中,是一个函数模板,

原型如下:

template class Fn, class... Args>

bind (Fn&& fn, Args&&... args);

调用bind的一般形式:auto newCallable = bind(callable,arg_list);

也是一个函数包装器(适配器),callabl代表可调用函数,arg_list用来接收绑定多个参数,以逗号分割,从而生成一个新的参数列表来替换原函数的参数列表,并将该bind函数对象赋值给newCallable,当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

arg_list中的参数可能包含形如_n的名字(属于placeholders类域),其中n是一个整数,这些参数是“占位符”,表示 newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示newCallable中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。概念还是抽象的,通过例子来演示:

#include <iostream>
using namespace std;
#include <functional>
int Plus(int a, int b)
{
	return a + b;
}
class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}
};
int main()
{
	//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定,并用function包装器包装bind
	function<int(int, int)> func1 = bind(Plus, placeholders::_2, placeholders::_1);//绑定参数,_2代表func1的第二个参数在第一个位置,_1代表func2的第一个参数在第二个位置
	//相当于第一个参数替换成了2,第二个替换成了1,即1传给了b,2传给了a
	cout << func1(1, 2) << endl;//调用Plus函数

	//auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2);
	

	//func2的类型为 function<void(int, int, int)> 与func1类型一样
	//表示绑定函数 plus 的第一,二为: 1, 2
	auto func2 = bind(Plus, 1, 2);//绑定参数,这些参数替换原来的参数,即func2的参数替换成了1,2
	cout << func2(2,3) << endl;//调用Plus函数


	// 绑定成员函数
	std::function<int(int, int)> func3 = std::bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);
	cout << func3(1, 2) << endl;


	std::function<int(int, int)> func4 = std::bind(&Sub::sub, Sub(), placeholders::_2, placeholders::_1);
	cout << func4(1, 2) << endl;
	return 0;
}

输出结果:

总结一句话,bind通过更改原函数参数列表来包装新函数。 

end~ 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

详解Amivest 流动性比率

详解Amivest 流动性比率 Claude-3.5-Sonnet Poe Amivest流动性比率是一个衡量证券市场流动性的重要指标。这个比率主要用于评估在不对价格造成重大影响的情况下,市场能够吸收多少交易量。以下是对Amivest流动性比率的详细解释: 定义: Amivest流动性比率是交易额与绝对收益率的…

一.2.(1)双极型晶体三极管的结构、工作原理、特性曲线及主要参数

1.双极型晶体三极管的结构 学会区分P管和N管&#xff0c;会绘制符号 2.工作原理 无论是PNP 还是NPN&#xff0c;本质上放大时&#xff0c;都是发射结正偏&#xff0c;集电极反偏。&#xff08;可以简单理解为pn为二极管&#xff0c;每个三极管都有两个二极管&#xff09; 其中电…

行内元素、块级元素居中

行内元素居中 水平居中 {text-align&#xff1a;center;}垂直居中 单行——行高等于盒子高度 <head><style>.father {width: 400px;height: 200px;/* 行高等于盒子高度&#xff1a;line-height: 200px; */line-height: 200px;background-color: pink;}.son {}&…

深入刨析Redis存储技术设计艺术(二)

三、Redis主存储 3.1、存储相关结构体 redisServer:服务器 server.h struct redisServer { /* General */ pid_t pid; /* Main process pid. */ pthread_t main_thread_id; /* Main thread id */ char *configfile; /* Absolut…

js获取当前浏览器地址,ip,端口号等等

前言&#xff1a; js获取当前浏览器地址&#xff0c;ip&#xff0c;端口号等等 window.location属性查询 具体属性&#xff1a; 1、获取他的ip地址 window.location.hostname 2、获取他的端口号 window.location.port 3、获取他的全路径 window.location.origin 4、获取…

EtherCAT转Profinet网关配置说明第一讲:配置软件安装及介绍

网关XD-ECPNS20为EtherCAT转Profinet协议网关&#xff0c;使EtherCAT协议和Profinet协议两种工业实时以太网网络之间双向传输 IO 数据。适用于具有EtherCAT协议网络与Profinet协议网络跨越网络界限进行数据交换的解决方案。 本网关通过上位机来进行配置。 首先安装上位机软件 一…

【日志信息管理】管理日志信息的类

日志用于记录程序的执行记录包括程序的出错记录&#xff0c;程序致命退出原因&#xff0c;程序的正常执行记录。这样我们就可以很快的察觉程序的错误原因、执行状况等等&#xff0c;因此管理日志信息是非常重要的。 日志一般由以下部分组合&#xff1a; 日志时间、日志等级、…

数据库可视化管理工具dbeaver试用及问题处理。

本文记录了在内网离线安装数据库可视化管理工具dbeaver的过程和相关问题处理方法。 一、下载dbeaver https://dbeaver.io/download/ 笔者测试时Windows平台最新版本为&#xff1a;dbeaver-ce-24.1.1-x86_64-setup.exe 二、安装方法 一路“下一步”即可 三、问题处理 1、问…

06浅谈大语言模型可调节参数TopP和TopK

浅谈大模型参数TopP和TopK 大语言模型中的temperature、top_p和top_k参数是用来控制模型生成文本时的随机性和创造性的。下面分享一下topP和topK两个参数的意义及逻辑&#xff1b; top K&#xff08;Top-K Sampling&#xff09; 作用&#xff1a;只从模型认为最可能的k个词中选…

排序-java(插入排序和选择排序)

一&#xff0c;分类 主要的排序大致分为以下几类&#xff1a; 1&#xff0c;插入排序&#xff0c;又分为直接插入排序和希尔排序 2&#xff0c;选择排序&#xff0c;又分为选择排序和堆排序 3&#xff0c;交换排序&#xff0c;又分为冒泡排序和快速排序 4&#xff0c;归并…

Python中异步事件触发

1、问题背景 在Python中&#xff0c;我想创建一个由事件生成控制流程的类结构。为此&#xff0c;我做了以下工作&#xff1a; class MyEvent: EventName_FunctionName {}classmethoddef setup(cls, notificationname, functionname):if notificationname in MyEvent.EventN…

如何借助AI在20分钟内写一个springboot单表的增删改查

目录 1. AI工具介绍2. 写代码的正确顺序2.1 编写 Entity 类&#xff1a;2.2 编写 Mapper 接口&#xff1a;2.3 编写 Mapper XML 文件&#xff08;如果使用 MyBatis&#xff09;&#xff1a;2.4 编写 Service 接口&#xff1a;2.5 编写 Service 实现类&#xff08;ServiceImpl&a…

【全面讲解如何安装Jupyter Notebook!】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

智慧校园综合解决方案PPT(41页)

1. 方案背景 智慧校园综合解决方案响应《教育信息化2.0行动计划》等政策&#xff0c;旨在加快智慧校园建设&#xff0c;推动信息化与学习生活的深度融合。目前教育信息化配套设施建设存在“孤岛架构”&#xff0c;学生安全问题频发&#xff0c;技术发展迅速&#xff0c;家长对…

IT高手修炼手册(3)程序员命令

一、前言 程序员在日常工作中&#xff0c;掌握一些高效的快捷键可以大大提高编码和开发效率。 二、通用快捷键 文本操作Ctrl A&#xff1a;全选当前页面内容 Ctrl C&#xff1a;复制当前选中内容 Ctrl V&#xff1a;粘贴当前剪贴板内的内容 Ctrl X&#xff1a;剪切当前选中…

[图解]SysML和EA建模住宅安全系统-11-接口块

1 00:00:00,660 --> 00:00:04,480 接下来的步骤是定义系统上下文 2 00:00:04,960 --> 00:00:07,750 首先是图17.17 3 00:00:09,000 --> 00:00:10,510 系统上下文展示了 4 00:00:10,520 --> 00:00:12,510 ESS和外部系统、用户 5 00:00:12,520 --> 00:00:14,1…

C++初学者指南-4.诊断---地址检测器

C初学者指南-4.诊断—地址检测器 幻灯片 地址检测器&#xff08;ASan&#xff09; 适用编译器g,clang检测内存错误 内存泄露访问已经释放的内存访问不正确的堆栈区域 用额外的指令检测代码 运行时间增加约70%内存使用量大约增加了3倍 示例&#xff1a;检测空指针 使用地址…

leetcode力扣_双指针问题

141. 环形链表 思路&#xff1a;判断链表中是否有环是经典的算法问题之一。常见的解决方案有多种&#xff0c;其中最经典、有效的一种方法是使用 快慢指针&#xff08;Floyd’s Cycle-Finding Algorithm&#xff09;。 初始化两个指针&#xff1a;一个快指针&#xff08;fast&…

100+大屏模板,基于Vue 国产开源 IoT 物联网 Web 组态可视化 BI 数据分析工具

项目源码&#xff0c;文末联系小编 01 DataEase 可视化大屏 DataEase 是一个国产开源的数据可视化分析工具(BI工具)&#xff0c;旨在帮助用户快速分析数据并洞察业务趋势&#xff0c;以实现业务的改进与优化。它支持丰富的数据源连接&#xff0c;包括OLTP和OLAP数据库、数据仓库…

19.JWT

1►JWT博客推荐 阮老师讲得很好了&#xff0c;网址如下&#xff1a; http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html 2►ry是怎么践行JWT的呢&#xff1f; 问题一&#xff1a;不登录的时候有token吗&#xff1f; 答&#xff1a;没有&#xff0c;所…