当前位置: 首页 > news >正文

【C++】C++11——包装器

文章目录

  • 1. function包装器
    • 1.1 遇到的问题
    • 1.2 包装器的定义
    • 1.3 解决问题
    • 1.4 包装器的其他应用
  • 2. bind
    • 2.1 bind的定义
    • 2.2 bind包装器绑定固定参数
    • 2.3 bind包装器调整传参顺序
    • 2.4 bind包装器的意义

1. function包装器

1.1 遇到的问题

我们首先来看一行代码:

ret = func(x);

假设这行代码能够正常运行,那么这个func是什么呢?函数名? 函数指针? 函数对象? 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;}
};
void Test1()
{cout << useF(f, 11.11) << endl;//函数名cout << useF(Functor(), 11.11) << endl;//函数对象cout << useF([](double d)->double {return d / 4; }, 11.11) << endl;//lambda表达式
}

image-20230727094927877

可以看到这里的useF函数模板被实例化了三份。但是使用function包装器就可以让只被实例化一份出来。


1.2 包装器的定义

function包装器,也叫做适配器。C++中的function本质是一个类模板

image-20230727092701982

模板参数说明:

  • Ret:被包装的可调用对象的返回值类型
  • Args…:可调用对象的参数包

如何使用包装器呢?

int func(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;}int plusd(int a, int b){return a + b;}
};
void Test2()
{//函数名(函数指针)function<int(int, int)> func1 = func;cout << func1(1, 2) << endl;//函数对象(仿函数)function<int(int, int)> func2 = Functor();cout << func2(1, 2) << endl;//lambda表达式function<int(int, int)> func3 = [](const int a, const int b)->int {return a + b; };cout << func3(1, 2) << endl;//类的静态成员函数function<int(int, int)> func4 = &Plus::plusi;//这里可以不加取地址符号cout << func4(1, 2) << endl;//类的成员函数function<int(Plus, int, int)> func5 = &Plus::plusd;//这里必须要加取地址符号,并且声明的时候需要显示的传类名cout << func5(Plus(), 1, 2) << endl;//调用的时候需要传对象
}

image-20230727101249743

1.3 解决问题

现在我们知道了包装器的用法了,那么怎么解决最开始的问题呢?

使用包装器包装可调用对象,然后将可调用对象传给useF

void Test3()
{function<double(double)> func1 = f;function<double(double)> func2 = Functor1();function<double(double)> func3 = [](double d)->double {return d / 4; };cout << useF(func1, 11.11) << endl;cout << useF(func2, 11.11) << endl;cout << useF(func3, 11.11) << endl;
}

image-20230727103219920

可以看到,这里的useF函数模板只实例化出了一个对象。

1.4 包装器的其他应用

我们之前做过一道题:逆波兰表达式求解150. 逆波兰表达式求值 - 力扣(LeetCode),当时使用的方法代码如下

class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> st;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();}
};

在这里我们使用switch语句来判断运算类型,还需要在前面的条件里面枚举出运算类型,非常麻烦,如果在工程中,这段代码的可维护性就非常差,需要在if语句中增加枚举,在switch语句中增加case语句,这种情况就可以使用包装器来简化代码

  • 首先建立运算符和执行函数之间的映射关系,当需要某一运算的时候就直接通过运算符找到对应的可调用对象调用即可。、
  • 当运算类型增加时,只需要增加运算符和执行函数之间的映射关系即可

那么修改后的代码如下

class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> st;map<string, function<int(int, int)>> opFuncMap = {{"+", [](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;}}};for(auto& str : tokens){if(opFuncMap.count(str) == 0){st.push(stoi(str));}else{int right = st.top();st.pop();int left = st.top();st.pop();st.push(opFuncMap[str](left, right));}}return st.top();}
};

2. bind

2.1 bind的定义

**bind时一个函数模板,就像是一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来”适应“原对象的参数列表。**一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用bind函数还可以实现参数顺序调整等操作。

image-20230727140931890

参数列表说明:

  • Fn:可调用对象
  • Ret:可调用对象的返回类型
  • Args:要绑定的参数列表:值或者时占位符

可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。调用bind的一般形式:auto newCallable = bind(callable,arg_list)

  • newCallable:生成的新的可调用对象

  • callable:需要包装的可调用对象

  • arg_list:逗号分隔的参数列表,对应给定的callable的参数,当调用newCallable的时候,newCallable会调用callable,并传给他arg_list中的参数。

一般使用bind调整参数位置的时候,会使用一个类placeholders,这是一个占位符类。

image-20230727142425111

表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置,比如_1为newCallable的第一个参数,_2为第二个参数,以此类推。

此外,除了用auto接收包装后的可调用对象,也可以用function类型指明返回值和形参类型后接收包装后的可调用对象。

2.2 bind包装器绑定固定参数

首先我们来看一种无意义的绑定

int Sub(int a, int b)
{return a - b;
}
void Test4()
{function<int(int, int)> func1 = bind(Sub, placeholders::_1, placeholders::_2);cout << func1(1, 3) << " " << Sub(1, 3) << endl;
}

绑定时第一个参数传入函数指针这个可调用对象,但后续传入的要绑定的参数列表依次是placeholders::_1和placeholders::_2,表示后续调用新生成的可调用对象时,传入的第一个参数传给placeholders::_1,传入的第二个参数传给placeholders::_2。此时绑定后生成的新的可调用对象的传参方式,和原来没有绑定的可调用对象是一样的,所以说这是一个无意义的绑定。

如果想让Sub的第二个参数固定绑定为10,就可以将绑定时的参数列表的palceholder::_2设置为10.

int Sub(int a, int b)
{return a - b;
}
void Test4()
{function<int(int)> func2 = bind(Sub, placeholders::_1, 10);cout << func2(2) << endl;
}

此时调用绑定后新生成的可调用对象时就只需要传入一个参数,它会将该值减10后的结果进行返回。

2.3 bind包装器调整传参顺序

同样的对于上面的Sub函数的例子,我们想让传入的参数顺序进行调换,也可以使用bind实现。将placeholder::_1和placeholder::_2的位置进行调换即可

void Test5()
{function<int(int, int)> func = bind(Sub, placeholders::_2, placeholders::_1);cout << func(1, 2) << endl;
}

根本原因就是因为,后续调用新生成的可调用对象时,传入的第一个参数会传给placeholders::_1,传入的第二个参数会传给placeholders::_2,因此可以在绑定时通过控制placeholders::_n的位置,来控制第n个参数的传递位置。

2.4 bind包装器的意义

  • 将一个函数的某些参数绑定为固定的值,让我们在调用时可以不用传递某些参数。
  • 可以对函数参数的顺序进行灵活调整。

本节完…

相关文章:

【C++】C++11——包装器

文章目录 1. function包装器1.1 遇到的问题1.2 包装器的定义1.3 解决问题1.4 包装器的其他应用 2. bind2.1 bind的定义2.2 bind包装器绑定固定参数2.3 bind包装器调整传参顺序2.4 bind包装器的意义 1. function包装器 1.1 遇到的问题 我们首先来看一行代码&#xff1a; ret …...

插件使用权限管理软件(三)WebAPI项目IIS部署

前言 前面完成了WebAPI项目的接口服务类编写工作&#xff0c;接下来讲把项目部署到服务器的IIS上&#xff0c;让系统运行起来。 一. 项目发布 右键项目RightsManagementSystems.Web.Entry 选择“发布”选项 弹出发布选项界面&#xff0c;选择“文件夹”&#xff0c;点击下一步…...

[算法很美打卡] 多维数组篇 (打卡第二天)

文章目录 Z形打印边界为1的最大子方阵 Z形打印 package 每日算法学习打卡.算法打卡.七月份.七月二十七号;public class test1 {public static void main(String[] args) {int[][] matrix {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12},};print(matrix);}static void print(int[…...

K8S初级入门系列之十一-安全

一、前言 安全是K8S重要的特性&#xff0c;在K8S初级入门系列之四-Namespace/ConfigMap/Secret章节&#xff0c;我们已经已经了解了Namespace&#xff0c;Secret与安全相关的知识。本篇将梳理K8S在安全方面的策略。主要包括两个方面&#xff0c;API安全访问策略以及Pod安全策略…...

【雕爷学编程】MicroPython动手做(02)——尝试搭建K210开发板的IDE环境6

#尝试搭建K210的Micropython开发环境&#xff08;Win10&#xff09; #实验程序之六&#xff1a;测试Microphone阵列算法 #尝试搭建K210的Micropython开发环境&#xff08;Win10&#xff09; #实验程序之六&#xff1a;测试Microphone阵列算法from Maix import MIC_ARRAY as mi…...

“深入解析Spring Boot:从入门到精通“

标题&#xff1a;深入解析Spring Boot&#xff1a;从入门到精通 摘要&#xff1a;本文深入解析了Spring Boot框架&#xff0c;从入门到精通&#xff0c;包括核心概念、特性、使用方法和示例代码。通过阅读本文&#xff0c;读者将对Spring Boot有一个全面的了解&#xff0c;并可…...

[自然语言处理] 自然语言处理库spaCy使用指北

spaCy是一个基于Python编写的开源自然语言处理库。基于自然处理领域的最新研究&#xff0c;spaCy提供了一系列高效且易用的工具&#xff0c;用于文本预处理、文本解析、命名实体识别、词性标注、句法分析和文本分类等任务。 spaCy的官方仓库地址为&#xff1a;spaCy-github。本…...

【新日语(2)】第6課 拓哉もさしみを食べたがってします

第6課 拓哉もさしみを食べたがっています 注释&#xff1a; 食べたがっています&#xff1a;食べ&#xff0b;たが&#xff0b;ています、想要吃。たがっています&#xff1a;たがる&#xff0b;ています、想要。 练习A 一、 例句 わたしは、明日、デパートへ行きます。 …...

uni-app 经验分享,从入门到离职(一)——初始 uni-app,快速上手(文末送书福利1.0)

文章目录 &#x1f4cb;前言&#x1f3af;什么是 uni-app&#x1f3af;创建第一个 uni-app 项目&#x1f9e9;前期工作&#x1f9e9;创建项目&#xff08;熟悉默认项目、结构&#xff09;&#x1f9e9;运行项目 &#x1f4dd;最后&#x1f3af;文末送书&#x1f525;参与方式 &…...

Python爬虫实例之淘宝商品页面爬取(api接口)

可以使用Python中的requests和BeautifulSoup库来进行网页爬取和数据提取。以下是一个简单的示例&#xff1a; import requests from bs4 import BeautifulSoupdef get_product_data(url):# 发送GET请求&#xff0c;获取网页内容headers {User-Agent: Mozilla/5.0 (Windows NT…...

并发编程 | CompletionService - 如何优雅地处理批量异步任务

引言 上一篇文章中&#xff0c;我们详细地介绍了 CompletableFuture&#xff0c;它是一种强大的并发工具&#xff0c;能帮助我们以声明式的方式处理异步任务。虽然 CompletableFuture 很强大&#xff0c;但它并不总是最适合所有场景的解决方案。 在这篇文章中&#xff0c;我们…...

医学案例|ROC曲线之面积对比

一、案例介绍 为评价CT和CT增强对肝癌的诊断效果&#xff0c;共检查了32例患者&#xff0c;每例患者分别用两种方法检查&#xff0c;由医生盲态按4个等级诊断&#xff0c;最后经手术病理检查确诊其中有16例患有肝癌&#xff0c;评价CT个CT增强对肝癌是有有诊断效果并且试着比较…...

Kotlin线程的基本用法

线程的基本用法 新建一个类继承自Thread&#xff0c;然后重写父类的run()方法 class MyThread : Thread() {override fun run() {// 编写具体的逻辑} }// 使用 MyThread().start()实现Runnable接口 class MyThread : Runnable {override fun run() {// 编写具体的逻辑} }// …...

2.03 PageHelper分页工具

步骤1&#xff1a;在application.yml中添加分页配置 # 分页插件配置 pagehelper:helperDialect: mysqlsupportMethodsArguments: true步骤2&#xff1a;在顶级工程pom文件下引入分页插件依赖 <!--5.PageHelper --> <dependency><groupId>com.github.pagehe…...

VUE中使用ElementUI组件的单选按钮el-radio-button实现第二点击时取消选择的功能

页面样式为&#xff1a; html 代码为&#xff1a; 日志等级&#xff1a; <el-radio-group v-model"logLevel"><el-radio-button label"DEBUG" click.native.prevent"changeLogLevel(DEBUG)">DEBUG</el-radio-button><el-r…...

瓴羊Quick BI:可视化大屏界面设计满足企业个性需求

大数据技术成为现阶段企业缩短与竞争对手之间差距的重要抓手&#xff0c;依托以瓴羊Quick BI为代表的工具开展内部数据处理分析工作&#xff0c;也成为诸多企业持续获取竞争优势的必由之路。早年间国内企业倾向于使用进口BI工具&#xff0c;但随着瓴羊Quick BI等一众国内数据处…...

617. 合并二叉树

题目 题解一&#xff1a;递归 /*** 递归* param root1* param root2* return*/public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {//结束条件if (root1 null) {return root2;} //结束条件if (root2 null) {return root1;}//两节点数值相加TreeNode me…...

【T1】存货成本异常、数量为零金额不为零的处理方法。

【问题描述】 使用T1飞跃专业版的过程中&#xff0c; 由于业务问题或者是操作问题&#xff0c; 经常会遇到某个商品成本异常不准确&#xff0c; 或者是遇到数量为0金额不为0的情况&#xff0c;需要将其成本调为0。 但是T1软件没有出入库调整单&#xff0c;并且结账无法针对数量…...

EtherNet IP转PROFINET网关连接西门子与欧姆龙方法

本文主要介绍了捷米特JM-PN-EIP&#xff08;EtherNet/IP转PROFINET&#xff09;网关西门子200智能PLC&#xff08;PROFINET&#xff09;和欧姆龙系统EtherNet/IP通信的配置过程。 1, 将 EDS 文件复制到欧姆龙软件的对应文件夹下 2, 首先添加捷米特JM-PN-EIP网关的全局变量&…...

低代码开发重要工具:jvs-flow(流程引擎)审批功能配置说明

流程引擎场景介绍 流程引擎基于一组节点与执行界面&#xff0c;通过人机交互的形式自动地执行和协调各个任务和活动。它可以实现任务的分配、协作、路由和跟踪。通过流程引擎&#xff0c;组织能够实现业务流程的优化、标准化和自动化&#xff0c;提高工作效率和质量。 在企业…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...