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传给包装器,就可以适配出lambdacout << 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包装器包装bindfunction<int(int, int)> func1 = bind(Plus, placeholders::_2, placeholders::_1);//绑定参数,_2代表func1的第二个参数在第一个位置,_1代表func2的第一个参数在第二个位置//相当于第一个参数替换成了2,第二个替换成了1,即1传给了b,2传给了acout << func1(1, 2) << endl;//调用Plus函数//auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2);//func2的类型为 function<void(int, int, int)> 与func1类型一样//表示绑定函数 plus 的第一,二为: 1, 2auto func2 = bind(Plus, 1, 2);//绑定参数,这些参数替换原来的参数,即func2的参数替换成了1,2cout << 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.xdnf.cn/news/1473983.html

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

相关文章

aardio —— 今日减bug

打字就减bug 鼠标双击也减bug 看看有多少bug够你减的 使用方法&#xff1a; 1、将资源附件解压缩&#xff0c;里面的文件夹&#xff0c;放到aardio\plugin\plugins 目录 2、aardio 启动插件 → 插件设置 → 选中“今日减bug” → 保存。 3、重启 aardio&#xff0c;等aa…

解决IDEA每次新建项目都需要重新配置maven的问题

每次打开IDEA都要重新配置maven&#xff0c;这是因为在DEA中分为项目设置和全局设置&#xff0c;这个时候我们就需要去到全局中设置maven了。我用的是IntelliJ IDEA 2023.3.4 (Ultimate Edition)&#xff0c;以此为例。 第一步&#xff1a;打开一个空的IDEA&#xff0c;选择左…

起飞,纯本地实时语音转文字!

简介 偶然在 github 上翻到了这个项目 https://github.com/k2-fsa/sherpa-ncnn 在没有互联网连接的情况下使用带有 ncnn 的下一代 Kaldi 进行实时语音识别。支持 iOS、Android、Raspberry Pi、VisionFive2、LicheePi4A等。 也就是说语音转文字可以不再借助网络服务的接口&am…

昇思MindSpore学习笔记4-03生成式--Diffusion扩散模型

摘要&#xff1a; 记录昇思MindSpore AI框架使用DDPM模型给图像数据正向逐步添加噪声&#xff0c;反向逐步去除噪声的工作原理和实际使用方法、步骤。 一、概念 1. 扩散模型Diffusion Models DDPM(denoising diffusion probabilistic model) &#xff08;无&#xff09;条件…

昇思25天学习打卡营第8天|模型权重与 MindIR 的保存加载

目录 导入Python 库和模块 创建神经网络模型 保存和加载模型权重 保存和加载MindIR 导入Python 库和模块 上一章节着重阐述了怎样对超参数予以调整&#xff0c;以及如何开展网络模型的训练工作。在网络模型训练的整个进程当中&#xff0c;事实上我们满怀期望能够留存中间阶段…

眼底图像生成新 SOTA:GeCA模拟生物细胞的演变过程

眼底图像生成新 SOTA&#xff1a;GeCA模拟生物细胞的演变过程 提出背景GeCA 框架生成元胞自动机&#xff1a;从单细胞到生物体的过程生物体从单个像素细胞开始细胞扩散&#xff1a;从细胞演变为生物体通过基因遗传改进逆向采样视网膜疾病分类GeCA 逻辑拆解子解法1&#xff1a;神…

Go高级库存照片源码v5.3

GoStock – 免费和付费库存照片脚本这是一个免费和付费共享高质量库存照片的平台,用户可以上传照片与整个社区和访客分享,并可以通过 PayPal 接收捐款。此外,用户还可以点赞、评论、分享和收藏您最喜欢的照片。 下载 特征: 使用Laravel 10构建订阅系统Stripe 连接渐进式网页…

【Python机器学习】模型评估与改进——分层k折交叉验证

在k折分层验证中&#xff0c;将数据集划分为k折时&#xff0c;从数据的前k分之一开始划分&#xff0c;这可能并不总是一个好主意&#xff0c;例如iris数据集中&#xff1a; from sklearn.datasets import load_irisirisload_iris() print(Iris labels:\n:{}.format(iris.targe…

2.Python学习:数据类型和变量

1.标识符命名规则 只能由数字、字母、下划线组成不能以数字开头不能是关键字&#xff08;如class等python内部已经使用的标识符&#xff09;区分大小写 查看关键字&#xff1a; print(keyword.kwlist)2.数据类型 2.1常见数据类型 2.1.1Number数值型&#xff1a; 整数int&a…

13 - Python网络编程入门

网络编程入门 计算机网络基础 计算机网络是独立自主的计算机互联而成的系统的总称&#xff0c;组建计算机网络最主要的目的是实现多台计算机之间的通信和资源共享。今天计算机网络中的设备和计算机网络的用户已经多得不可计数&#xff0c;而计算机网络也可以称得上是一个“复…

自定义isdate函数,判定日期字符串有效性

自定义isdate函数&#xff0c;按日期“属性”&#xff0c;判定字符串日期有效性。 (笔记模板由python脚本于2024年07月05日 15:28:04创建&#xff0c;本篇笔记适合喜欢探究python内建模块的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.o…

One day for Chinese families

周围生活中的普通家庭的一天流程&#xff1a; 【上班的一天】 【放假的一天】 有家庭的人&#xff0c;上班流程&#xff1a; 01&#xff09;准备早餐&#xff0c;牛奶&#xff0c;面包 02&#xff09;叫娃娃起床&#xff0c;一般要蛮久的&#xff1b;沟通交流 -- 哄娃娃 -- 生气…

2-5 softmax 回归的简洁实现

我们发现通过深度学习框架的高级API能够使实现线性回归变得更加容易。 同样&#xff0c;通过深度学习框架的高级API也能更方便地实现softmax回归模型。 本节如在上节中一样&#xff0c; 继续使用Fashion-MNIST数据集&#xff0c;并保持批量大小为256。 import torch from torc…

Linux操作系统的引导过程

系统初始化进程与文件、systemd概述、单元类型、切换运行级别、查看系统默认默认运行、永久切换、常见的系统服务&#xff08;centos&#xff09;-CSDN博客 centos 7系统升级内核&#xff08;ELRepo仓库&#xff09;、小版本升级、自编译内核-CSDN博客 ss命令详细使用讲解文…

微积分-导数4(三角函数的导数)

证明 f ( x ) sin ⁡ x f(x) \sin x f(x)sinx的导数为 f ′ ( x ) cos ⁡ x f(x) \cos x f′(x)cosx 已知函数 f ( x ) sin ⁡ x f(x) \sin x f(x)sinx 画出 f ( x ) f(x) f(x)图像以及 f ′ ( x ) f(x) f′(x)的图像 因此&#xff0c;我们可以合理的猜测&#xff1a;…

每日一题~ (判断是否是合法的出栈序列)

大概的题意&#xff1a; 将 1-n 按照顺序进栈&#xff0c;问 输入的序列是否是合法的出栈序列。 遍历序列&#xff0c;如果当前这个值a小于 栈顶的值&#xff0c;说明它还未进栈&#xff08;因为我们是按照顺序进栈的&#xff09;&#xff0c;所以我们将 一些元素进栈&#xff…

最短路:Dijkstra

原始模板&#xff1a; 时间复杂度O() 使用于图很满的情况 struct Node{int y,v;Node(int _y,int _v){y_y;v_v;} };vector<Node> edge[N1]; int n,m,dist[N1]; bool b[N1];int Dijistra(int s,int t){memset(b,false,sizeof(b));memset(dist,127,sizeof(dist));dist[s]…

Linux开发讲课33---线程实现与线程控制步骤简析

线程概述 进程是系统中程序执行和资源分配的基本单位。 每个进程都拥有自己的数据段、代码段和堆栈段&#xff0c;这就造成了进程在进行切换等操作时都需要有比较负责的上下文切换等动作。为了进一步减少处理机的空转时间支持多处理器和减少上下文切换开销&#xff0c;进程在演…

第5章 认证授权:需求分析,Security介绍(OAuth2,JWT),用户认证,微信扫码登录,用户授权

1 模块需求分析 1.1 什么是认证授权 截至目前&#xff0c;项目已经完成了课程发布功能&#xff0c;课程发布后用户通过在线学习页面点播视频进行学习。如何去记录学生的学习过程呢&#xff1f;要想掌握学生的学习情况就需要知道用户的身份信息&#xff0c;记录哪个用户在什么…

工作手机怎么做好业务员工作微信的监控管理

什么是工作手机管理系统&#xff1f; 工作手机管理系统是专为企业管理设计的员工微信管理&#xff0c;它通过监控通讯记录、保障数据安全、自动检测敏感行为、永久保留客户信息等功能&#xff0c;帮助企业提升销售效率、维护客户资源安全&#xff0c;并确保业务流程的合规性。…