function包装器和bind包装器

function包装器和bind包装器

  • 包装器
  • function包装器
    • 为什么需要function
    • function包装器
    • function包装器的应用场景
      • 逆波兰表达式求值
  • bind包装器
    • bind包装器的应用场景

包装器

包装器是用于给其他编程接口提供更一致或更合适的接口

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

function包装器

function是一种函数包装器,也叫做适配器。它可以对可调用对象进行包装,C++中的function本质就是一个类模板

为什么需要function

我们看如下例子:

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;}
};void test1()
{// 函数名cout << useF(f, 11.11) << endl;// 仿函数cout << useF(Functor(), 11.11) << endl;// lamber表达式cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
}

useF中的f(x),f可能是什么呢?函数名?函数指针?仿函数对象?lambda表达式对象?这些都是可调用的类型对象,这就会导致模板的效率低下。怎么个低下法呢?我们执行上述代码,得到结果:在这里插入图片描述

可以看到,上述代码产生了三个不同的静态变量count,说明useF函数模板被实例化出了三份。之所以被实例化出三份,是因为fFunctor()[](double d)->double { return d / 4; }的类型并不相同。

那么为了提高效率,能不能让useF函数模板只实例化一份呢?换句话说,能不能让这三个类型都统一成一个类型?–function包装器

function包装器

std::function本质是个类模板,在头文件<functional>

template <class Ret, class... Args>
class function<Ret(Args...)>;		//Args...是参数列表
  • Ret: 被调用函数的返回类型
  • Args…:被调用函数的形参

根据上面的例子,被调用的函数是double f(double i),因此Ret和Args都是double

所以void test1()中对于useF的调用可以写成这样:

void test1()
{// 函数名function<double(double)> f1 = f;// 仿函数对象function<double(double)> f2 = Functor();// lamber表达式对象function<double(double)> f3 = [](double d)->double { return d /4; };cout << useF(f1, 11.11) << endl;cout << useF(f2, 11.11) << endl;cout << useF(f3, 11.11) << endl;
}

运行结果如下:在这里插入图片描述

可以看到只生成了一个静态变量count,说明只实例化出了一份函数。这样就提高了效率

function包装器的应用场景

  • 场景一:上面所讲的提高模板的使用效率
  • 场景二:将可调用对象的类型统一

针对场景二,进行详细讲解:

为什么要将这些可调用对象的类型进行统一呢?因为统一后,可以将不同的可调用对象都存储在同一个容器中,方便管理

以前想把那些可调用对象存在同一个vector中是几乎不可能的,现在借助包装器,将可调用对象类型统一后,就可以存储了

比如,延续上面的例子:

	// 函数名function<double(double)> f1 = f;// 仿函数对象function<double(double)> f2 = Functor();// lamber表达式对象function<double(double)> f3 = [](double d)->double { return d / 4; };//写法一:vector<function<double(double)>> v = { f1,f2,f3 };//写法二:直接写:vector<function<double(double)>> v = { f,Functor(),[](double d)->double { return d / 4; } };

下面给出一个实例:

逆波兰表达式求值

150. 逆波兰表达式求值 - 力扣(LeetCode)

在这里插入图片描述

在这里插入图片描述

  • 以前的做法:
    int evalRPN(vector<string>& tokens) {stack<int> st;//将tokens中的数字依次入栈,若遇到操作符则出栈计算for(auto& str:tokens){if(str=="+" || str=="-" || str=="*" || str=="/"){int right=st.top();st.pop();int left=st.top();st.pop();switch(str[0]){case '+':st.push(left+right);break;case '-':st.push(left-right);break;case '*':  st.push(left*right);break;case '/':st.push(left/right);break;}}else{st.push(stoi(str));//将字符串变成整型存入栈中}}return st.top();}
  • 现在利用function包装器实现:

这里是一个命令对应一个动作,比如遇到”+“,就实现两个数相加。

(凡是类似这种场景,都可以利用包装器。就比如说linux的命令,就可以这样实现)

    int evalRPN(vector<string>& tokens) {map<string,function<int(int,int)>> m ={{"+",[](int x,int y){return x+y;}},{"-",[](int x,int y){return x-y;}},{"*",[](int x,int y){return x*y;}},{"/",[](int x,int y){return x/y;}}};stack<int> st;//将tokens中的数字依次入栈,若遇到操作符则出栈计算for(auto& str:tokens){if(m.count(str)){int right=st.top();st.pop();int left=st.top();st.pop();st.push(m[str](left,right));}else{st.push(stoi(str));//将字符串变成整型存入栈中}}return st.top();}

上述代码还有一个优势,就是如果再增加运算符操作的话,只需在map这增加代码即可,其他地方不需要改动

这里为了简便就用了lambda表达式。当然也可以写函数名、仿函数对象,如下:

class Solution 
{
public:static int add(int x, int y){return x+y;}struct sub{int operator()(int x,int y){return x-y;}};int evalRPN(vector<string>& tokens) {map<string,function<int(int,int)>> m ={{"+",add},{"-",sub()},{"*",[](int x,int y){return x*y;}},{"/",[](int x,int y){return x/y;}}};//……}
};

int add(int x, int y)前面要加一个static是因为:这个函数是在类中,类成员函数一般都是由对象去调用的,这里只想要用它的地址 加上静态关系后,就不需要依靠对象调用了,光写函数名就能代表函数的地址了

bind包装器

bind也是一种函数包装器,也叫做适配器。它可以接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。 C++中的bind本质是一个函数模板,也在头文件<functional>

bind包装器的应用场景

  1. 调整函数的参数顺序

我们看如下代码:

int Sub(int x, int y)
{return x - y;
}int main()
{function<int(int, int)> rSub1 = bind(Sub, placeholders::_1, placeholders::_2);cout << rSub1(10, 5) << endl;function<int(int, int)> rSub2 = bind(Sub, placeholders::_2, placeholders::_1);cout << rSub2(10, 5) << endl;return 0;
}

这里的placeholders是个命名空间,_1_2都是命名空间中的标识符

运行结果:在这里插入图片描述

我们看改变的地方只有一处:在这里插入图片描述

这就是利用bind实现的参数顺序的调换

我们理一下调用逻辑:

  • 对于rSub1:

    在这里插入图片描述

  • 对于rSub2:

    在这里插入图片描述

总结:

  • bind过后的函数传参时:第一个实参只会传给标识符_1,第二个实参只会传给标识符_2,以此类推

    这里传几个实参,bind语句中就有几个_,且是对应死的。第一个实参就对应于_1

    所以说,在上面写个_3是会报错的

  • bind传给原函数时:不再看什么标识符,按照位置关系进行传参

  1. 调整函数的传参个数(也就是将原本函数中的某一个参数绑定死)

看如下代码:

double Plus(int x, int y, double rate)
{return (x + y) * rate;
}int main()
{function<double(int, int)> plus1 = bind(Plus, placeholders::_1, placeholders::_2, 4.1);function<double(int, int)> plus2 = bind(Plus, placeholders::_1, placeholders::_2, 4.3);function<double(int, int)> plus3 = bind(Plus, placeholders::_1, placeholders::_2, 4.5);cout << plus1(5, 3) << endl;cout << plus2(5, 3) << endl;cout << plus3(5, 3) << endl;return 0;
}

这里就是将Plus函数的第三个参数rate固定

尽管缺省参数也可以实现将参数固定,但缺省参数只能固定一种情况,bind可以固定任意情况

此外,还要注意在写类型时,被绑定的参数的类型就不需要写在function中了

假如我最初函数写成这样:

在这里插入图片描述

那么bind语句就这样写:

在这里插入图片描述

注意这里还是_1和_2。因为这个是和bind后的函数的第几个实参对应的。

  1. 绑定类的成员函数

看如下代码:

class SubType
{
public:int sub(int x, int y){return x - y;}static  int ssub(int a, int b, int rate){return (a - b) * rate;}
};int main()
{//bind静态成员函数function<int(int, int)> rssub = bind(SubType::ssub, placeholders::_1, placeholders::_2,3);cout << rssub(5, 3) << endl;//bind成员函数function<int(int, int)> rsub = bind(&SubType::sub,SubType(), placeholders::_1, placeholders::_2);cout << rsub(5, 3) << endl;return 0;
}

注意点:

  • 绑定类成员函数时,一定要声明这个函数在哪个类中,如上SubType::
  • bind成员函数时,需要在函数名前面加上&(静态成员函数可加可不加)
  • bind成员函数时,需要增加一个参数:类对象或者类对象的指针

其实,绑定的底层也是仿函数

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

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

相关文章

Java_POJO

概念 POJO即简单的Java对象&#xff0c;区别于JavaBean JavaBean&#xff1a;特殊的Java类&#xff0c;容易被重用或插入到其他应用程序中去&#xff0c;通过封装属性和方法成为具有某种功能或者处理某个业务的对象 这个类必须有public的无参构造器所有属性都是private的所有属…

【React】极客园--04.发布文章模块

实现基础文章发布 创建基础结构 import {Card,Breadcrumb,Form,Button,Radio,Input,Upload,Space,Select } from antd import { PlusOutlined } from ant-design/icons import { Link } from react-router-dom import ./index.scssconst { Option } Selectconst Publish () …

使用Java构建微服务架构的最佳实践

使用Java构建微服务架构的最佳实践 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 随着云计算、容器化和微服务架构的兴起&#xff0c;越来越多的企业开始采用…

【JavaEE】Cookie和Session详解

一.Cookie 首先我们知道HTTP协议本身是’‘无状态’‘的, 这里的’‘无状态’指的是:默认情况下HTTP协议的客户端和服务器之间的这次通信,和下次通信之间没有直接的联系. 但是在实际的开发过程之中, 我们很多时候是需要知道请求之间的关联关系的. 例如登陆网站成功后,第二次访…

[modern c++][17] 任意数据类型 any

前言&#xff1a; #include <any> c17里引入了std::any类型&#xff0c;这个类型类似于union类型&#xff0c;但是比union功能多且能容纳所有类型(内置类型/自定义类型)&#xff0c;同时提供 type 方法用于获取一个 type_info 实例&#xff0c;进而用于准确判断入参的准…

IPv6知识点整理

IPv6&#xff1a;是英文“Internet Protocol Version 6”&#xff08;互联网协议第6版&#xff09;的缩写&#xff0c;是互联网工程任务组&#xff08;IETF&#xff09;设计的用于替代IPv4的下一代IP协议&#xff0c;其地址数量号称可以为全世界的每一粒沙子编上一个地址 。 国…

BigDataCloud 反向地理编码

在当今数字化飞速发展的时代&#xff0c;地理信息的精确获取和游戏数据的深入分析成为众多领域的关键需求。2024 年的今天&#xff0c;技术的创新为我们带来了更为出色的 API 服务。BigDataCloud 反向地理编码服务&#xff0c;能够将经纬度迅速而准确地转换为详细位置信息&…

ThinkPHP5大学生社会实践管理系统

有需要请加文章底部Q哦 可远程调试 ThinkPHP5大学生社会实践管理系统 一 介绍 大学生社会实践管理系统基于ThinkPHP5框架开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈&#xff1a;ThinkPHP5mysqlbootstrapphpstudyvscode 二 功…

ChatTTS增强版V3【已开源】,长文本修复,中英混读,导入音色,批量SRT、TXT

ChatTTS增强版V3来啦&#xff01;本次更新增加支持导入SRT、导入音色等功能。结合上次大家反馈的问题&#xff0c;修复了长文本、中英混读等问题。 项目已开源(https://github.com/CCmahua/ChatTTS-Enhanced) 项目介绍 V3 ChatTTS增强版V3&#xff0c;长文本修复&#xff0c…

找不到xinput1_3.dll如何修复?总结几种靠谱的修复方法

在数字时代&#xff0c;软件问题几乎是每个电脑用户都会遇到的难题。最近&#xff0c;我也遇到了一个令人头疼的问题——xinput1_3.dll文件丢失。这个问题导致我无法正常运行一些游戏&#xff0c;十分影响我的娱乐体验。通过这次修复经历&#xff0c;我不仅解决了问题&#xff…

8个腾讯,18个阿里,104个百度

8个腾讯、18个阿里巴巴、104个百度!英伟达市值已经超越我的前司微软&#xff0c;成为全球第一&#xff0c;&#xff08;虽然今天又被微软超越&#xff0c;但势头非常猛&#xff09;达到了恐怖的3.34万亿美元&#xff01;这是什么概念&#xff1f;相当于8个腾讯&#xff0c;18个…

逆向学习COM篇:通过注册表管理COM组件

本节课在线学习视频&#xff08;网盘地址&#xff0c;保存后即可免费观看&#xff09;&#xff1a; ​​https://pan.quark.cn/s/a1b4228ba501​​ 在Windows操作系统中&#xff0c;COM&#xff08;Component Object Model&#xff09;组件的注册和反注册是开发和维护过程中的…

ES6+Vue

ES6Vue ES6语法 ​ VUE基于是ES6的&#xff0c;所以在使用Vue之前我们需要先了解一下ES6的语法。 1.什么是ECMAScript6 ECMAScript是浏览器脚本语言的规范&#xff0c;基于javascript来制定的。为什么会出现这个规范呢&#xff1f; 1.1.JS发展史 1995年&#xff0c;网景工…

【第25章】Vue实战篇之用户登出

文章目录 前言一、后端代码二、前端代码1.接口调用2.界面代码3.事件代码 三、效果总结 前言 这里来演示用户登出。 一、后端代码 /*** 登出* param token token* return Result*/RequestMapping("logout")public Result logout(RequestHeader("Authorization&…

LeetCode26. 删除有序数组中的重复项题解

LeetCode26. 删除有序数组中的重复项题解 题目链接&#xff1a; https://leetcode.cn/problems/remove-duplicates-from-sorted-array 题目描述&#xff1a; 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一…

Excel 如何复制单元格而不换行

1. 打开excle, sheet1右键单击>查看代码>插入>模块 输入代码 Sub CopyText() Updated by NirmalDim xAutoWrapper As ObjectSet xAutoWrapper New DataObject or GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")xAutoWrapper.SetText ActiveC…

Depth Anything V2:抖音开源高性能任何单目图像深度估计V2版本,并开放具有精确注释和多样化场景的多功能评估基准

&#x1f4dc;文献卡 题目&#xff1a; Depth Anything V2作者: Lihe Yang; Bingyi Kang; Zilong Huang; Zhen Zhao; Xiaogang Xu; Jiashi Feng; Hengshuang ZhaoDOI: 10.48550/arXiv.2406.09414摘要: This work presents Depth Anything V2. Without pursuing fancy technique…

RN组件库 - Button 组件

从零构建 React Native 组件库&#xff0c;作为一个前端er~谁不想拥有一个自己的组件库呢 1、定义 Button 基本类型 type.ts import type {StyleProp, TextStyle, ViewProps} from react-native; import type {TouchableOpacityProps} from ../TouchableOpacity/type; import…

webpack安装sass

package.json文件 {"devDependencies": {"sass-loader": "^7.2.0","sass": "^1.22.10","fibers": "^4.0.1"} }这个不用webpack.config.js module.exports {module: {rules: [{test: /\.s[ac]ss$/i,u…

FlinkSQL开发经验分享

最近做了几个实时数据开发需求&#xff0c;也不可避免地在使用Flink的过程中遇到了一些问题&#xff0c;比如数据倾斜导致的反压、interval join、开窗导致的水位线失效等问题&#xff0c;通过思考并解决这些问题&#xff0c;加深了我对Flink原理与机制的理解&#xff0c;因此将…