C++进阶--C++11包装器

目录

  • 一、function包装器
    • 1.1 function包装器的概念
    • 1.2 function包装器实例化(统一类型)
    • 1.4 function包装器的意义
  • 二、bind包装器
    • 2.1 bind包装器的概念
      • 2.1.1 bind包装器
      • 2.1.2 调用bind的一般形式
    • 2.2 bind包装器绑定固定参数
      • 2.2.1 无意义的绑定
      • 2.2.2 绑定固定参数
    • 2.3 bind包装器调整传参顺序
    • 2.4 bind包装器的意义

一、function包装器

1.1 function包装器的概念

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

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

  模板参数说明:
Ret:被包装的可调用对象的返回值类型。
Ret:被包装的可调用对象的形参类型。

  function包装器可以对可调用对象进行包装,包括函数指针(函数名)、仿函数(函数对象)、lambda表达式、类的成员函数。

#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()
{// 函数名(函数指针)std::function<int(int, int)> func1 = f;cout << func1(1, 2) << endl;// 函数对象std::function<int(int, int)> func2 = Functor();cout << func2(1, 2) << endl;// lamber表达式std::function<int(int, int)> func3 = [](const int a, const int b){return a + b; };cout << func3(1, 2) << endl;// 类的成员函数std::function<int(int, int)> func4 = &Plus::plusi;cout << func4(1, 2) << endl;std::function<double(Plus, double, double)> func5 = &Plus::plusd;cout << func5(Plus(), 1.1, 2.2) << endl;return 0;
}

注意
包装时指明返回值类型和各形参类型,然后将可调用对象赋值给function包装器即可,包装后function对象就可以像普通函数一样使用了。
取静态成员函数的地址可以不用取地址运算符“&”,但取非静态成员函数的地址必须使用取地址运算符“&”。
包装非静态的成员函数时需要注意,非静态成员函数的第一个参数是隐藏this指针,因此在包装时需要指明第一个形参的类型为类的类型。

1.2 function包装器实例化(统一类型)

  包装器可以解决模板实例化多份而造成的效率低下的问题。

  • 在useF模板中,有两个参数,第一个参数可以接收各种函数类型,第二个参数可以接收各种信息。
  • useF函数模板中的定义了一个静态变量,如果实例化出多分useF函数模板,静态变量count也将不是同一个变量。
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;//仿函数cout << useF(Functor(), 11.11) << endl;//lambda表达式cout << useF([](double d)->double {return d / 4; }, 11.11) << endl;return 0;
}

  由此可见,在这里由于我们对useF函数模板的第一个参数T传递了三种不同类型的函数,进而导致useF函数模板在编译阶段实例化出了三份,因为静态变量为三个不同的变量。
   在使用三种不同函数作为实参传递并调用useF函数模板,实际上是分别调用了一次不同的useF函数,进而导致useF函数中的静态变量不是同一个,所以count只增加了一次。
  但是由于我们所传的三种函数类型的返回值数据类型和形参数据类型是一样的,在执行useF函数中,其他数据类型也相同,所以根本就没必要实例化三份useF函数。

所以,包装器可以解决因为该情况而导致模板实例化多份造成效率低下的问题

int main()
{//函数名function<double(double)> func1 = func;cout << useF(func1, 11.11) << endl;//函数对象function<double(double)> func2 = Functor();cout << useF(func2, 11.11) << endl;//lambda表达式function<double(double)> func3 = [](double d)->double {return d / 4; };cout << useF(func3, 11.11) << endl;return 0;
}

  由于useF类模板中的静态变量相同,由此可以判断经过function包装过后,useF函数模板只实例化出了一份,这极大地提高了函数模板的效率。
  当我们传递三种不同的函数类型并进行调用时,实际上就是调用了三次同一个useF函数,对静态变量count累加了三次

1.4 function包装器的意义

  • 将可调用对象的类型进行统一,便于我们对其进行统一化管理。
  • 包装后明确了可调用对象的返回值和形参类型,更加方便使用者使用。

二、bind包装器

2.1 bind包装器的概念

2.1.1 bind包装器

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

bind函数模板的原型:

template <class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);
template <class Ret, class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);

参数说明

  • fn:可调用对象。
  • args…:要绑定的参数列表:值或占位符。

2.1.2 调用bind的一般形式

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

  • callable:需要包装的可调用对象。
  • newCallable:生成的新的可调用对象。
  • arg_list:逗号分隔的参数列表,对应给定的callable的参数。当调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

  arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置,比如_1为newCallable的第一个参数,_2为第二个参数,以此类推。
  此外,除了用auto接收包装后的可调用对象,也可以用function类型指明返回值和形参类型后接收包装后的可调用对象。

2.2 bind包装器绑定固定参数

2.2.1 无意义的绑定

int Plus(int a, int b)
{return a + b;
}
int main()
{//表示绑定函数plus参数分别由调用func1的第一,二个参数指定std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1, placeholders::_2);cout << func1(10, 20) << endl;return 0;
}

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

2.2.2 绑定固定参数

  如果想把Plus函数的第二个参数固定绑定为10,可以在绑定时将参数列表的placeholders::_2设置为10。

#include <functional>
int Plus(int a, int b)
{return a + b;
}
int main()
{function<int(int)> func = bind(Plus, placeholders::_1,10);cout << func(2) << endl;//12return 0;
}

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

2.3 bind包装器调整传参顺序

  对于下面Sub类中的sub成员函数,sub成员函数的第一个参数是隐藏的this指针,如果想要在调用sub成员函数时不用对象进行调用,那么可以将sub成员函数的第一个参数固定绑定为一个Sub对象。

class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{//绑定固定参数function<int(int, int)> func = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);cout << func(1, 2) << endl; //-1return 0;
}

  此时调用绑定后生成的可调用对象时,就只需要传入用于相减的两个参数了,因为在调用时会固定帮我们传入一个匿名对象给this指针。
  如果想要将sub成员函数用于相减的两个参数的顺序交换,那么直接在绑定时将placeholders::_1和placeholders::_2的位置交换一下就行了。

class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{//调整传参顺序function<int(int, int)> func = bind(&Sub::sub, Sub(), placeholders::_2, placeholders::_1);cout << func(1, 2) << endl; //1return 0;
}

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

2.4 bind包装器的意义

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

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

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

相关文章

Redis系列——Lua脚本和redis事务的应用

介绍 Lua脚本 背景 Redis是一种抽象数据类型的特定领域语言&#xff0c;由各种命令组成。大多数命令专门用于操作不通的数据类型。每次发送命令均需要执行至此网络请求。所以Redis提供了一个编程接口&#xff0c;支持服务器执行用户自定义的任意脚本。有助于减少网络流量&am…

linux文件的IO函数

open函数: 作用&#xff1a;打开或者新建一个文件 原型&#xff1a; int open(const char*pathname,int flags); int open(const char*pathname,int flags,mode_t mode); 参数&#xff1a; pathname:路径 flags:1-> O_RONLY 只读打开 2 -> O_WONLY只写打开 3->…

83 CTF夺旗-Python考点SSTI反序列化字符串

这里写目录标题 CTF各大题型简介演示案例:CTF夺旗-Python-支付逻辑&JWT&反序列化CTF夺旗-Python-Flask&jinja2&SSTl模版注入CTF夺旗-Python-格式化字符串漏洞&读取对象 涉及资源&#xff1a; 我们这篇文章主要讲的是CTF在web渗透测试方向的3个考点 CTF各大…

elementui常用组件-个人版(间断更新)

Dialog 对话框 el-dialog <el-dialogtitle"提示":visible.sync"dialogVisible"width"30%":before-close"handleClose"><span>这是一段信息</span><span slot"footer" class"dialog-footer"…

代码随想录算法训练营|day25

第七章 回溯算法 216.组合总和III17.电话号码的字母组合代码随想录文章详解总结 216.组合总和III 回溯&#xff1a;i宽度约束&#xff0c;sum深度约束 func combinationSum3(k int, n int) [][]int {res : [][]int{}path : []int{}var help func(startIndex, sum, k, n int)h…

开源一套直播平台

博主开源一套直播平台,私信博主可获取源码 直播平台系统界面介绍

微信小程序(三十六)事件传参

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.传参步骤 2.传参接收解构步骤 源码&#xff1a; index.wxml <button type"primary" bind:tap"onclick" mark:index"{{0}}" mark:remb"{{1}}" class"But&quo…

产品经理--分享在项目中产品与研发之间会遇到的问题 在面试这一岗位时,面试官常问的问题之一,且分享两大原则来回答面试官这一问题

目录 一.STAR原则 1.1 简介 1.2 如何使用 1.3 举例说明 二.PDCA原则 2.1 简介 2.2 如何使用 2.3 运用场景 2.4 举例说明 三.产品与研发的沟通痛点 3.1 沟通痛点的原因 3.2 分享案例 前言 本篇会详细阐明作为一个产品经理会在项目遇到的问题&#xff0c;如&#xff1a;产…

力扣_字符串4—编辑距离

题目 给你两个单词 w o r d 1 word1 word1 和 w o r d 2 word2 word2&#xff0c; 请返回将 w o r d 1 word1 word1 转换成 w o r d 2 word2 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 方法—动…

记录 | python 源码编译报错subprocess.CalledProcessError Command lsb_release

subprocess.CalledProcessError: Command (lsb_release, -a) returned non-zero exit status 1. 解决方法 find / -name lsb_releaserm -rf /usr/bin/lsb_release

张艺谋《主角》选角引发热议,周迅、赵丽颖、杨紫或成候选。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 近日&#xff0c;张艺谋执导的首部电视剧《主角》女主选角成为…

6.electron之上下文隔离,预加载JS脚本

如果可以实现记得点赞分享&#xff0c;谢谢老铁&#xff5e; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron 将 Chromium 和 Node.js 嵌入到了一个二进制文件中&#xff0c;因此它允许你仅需一个代码仓库&#xff0c;就可以撰写支持 Windows、…

【Linux】vim的基本操作与配置(上)

Hello everybody!今天我们要进入vim的讲解了。学会了vim,咱们就可以在Linux系统上做一些简单的编程啦&#xff01; 那么废话不多说&#xff0c;咱们直接进入正题&#xff01; 1.初识vim vim是一款多模式的文本编辑器&#xff0c;可以对一个文件进行编辑操作。 它一共有三个模…

2024年【化工自动化控制仪表】最新解析及化工自动化控制仪表考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【化工自动化控制仪表】最新解析及化工自动化控制仪表考试资料&#xff0c;包含化工自动化控制仪表最新解析答案和解析及化工自动化控制仪表考试资料练习。安全生产模拟考试一点通结合国家化工自动化控制仪表考…

《dx12 龙书》第四部分学习笔记——预备知识(下)

7、多重采样技术的原理 由于屏幕中显示的像素不可能是无穷小的&#xff0c;所以并不是任意一条直线都能在显示器上“平滑”而完美地呈现出来。即为以像素矩阵 &#xff08;matrix of pixels&#xff0c; 可以理解为“像素2D数组”&#xff09;逼近直线的方法所产生的“阶梯” &…

EDM营销平台哪个好?推荐的邮件营销平台?

EDM邮件营销平台有哪些&#xff1f;外贸EDM邮件营销平台有哪些&#xff1f; EDM营销平台已成为企业推广产品和服务的重要工具。但是&#xff0c;面对市场上众多的EDM营销平台&#xff0c;究竟哪个更好呢&#xff1f;下面&#xff0c;蜂邮EDM将从平台功能、用户体验、数据分析和…

关于五子棋算法的学习

基于python的AI五子棋实现&#xff08;极大极小值搜索和alpha beta剪枝&#xff09;_pygame五子棋ai算法-CSDN博客 csdn搜索&#xff1a;AI五子棋实现 AI人机对战五子棋游戏【Python(pygame)AI】并实现软件输出_python设计两个ai对战黑白棋-CSDN博客z​​​​​​​z这个很详细…

Matlab使用点云工具箱进行点云配准

一、代码 source_pc pcread(bun_zipper.ply); target_pc pcread(bun_zipper2.ply); % 下采样 gridStep 0.001; ptCloudA pcdownsample(source_pc,gridAverage,gridStep); ptCloudB pcdownsample(target_pc,gridAverage,gridStep); % 初始变换矩阵 tform_initial affine3…

第三百一十二回

文章目录 1. 概念介绍2. 实现方法2.1 obscureText属性2.2 decoration属性 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何实现倒计时功能"相关的内容&#xff0c;本章回中将介绍如何实现密码输入框.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍…

C语言笔试题之实现C库函数 pow()(递归的思想)

实例要求&#xff1a; 1、请你实现C库函数 pow()&#xff08;stdio.h & math.h&#xff09; &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即x^n &#xff09;&#xff1b;2、函数声明&#xff1a;double myPow(double x, int n)&#xff1b;参数&#xff1a;1、x …