C++11 包装器function
文章首发公众号:iDoitnow
C++提供了多个包装器,它们主要是为了给其他编程接口提供更一致或更合适的接口。C++11提供了多个包装器,这里我们重点了解一下包装器function
。
对于function
, C++ 参考手册给出的定义为:
类模板
std::function
是通用多态函数封装器。std::function
的实例能存储、复制及调用任何可调用 (Callable) 目标——函数、 lambda 表达式、 bind 表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针。
C++11为什么要引入function
我们先看一个例子:
#include <iostream>template <typename T, typename F>
T use_f(T v, F f) {static int count = 0;count++;std::cout << " use_f count = " << count << ", &count = " << &count<< std::endl;return f(v);
}class Fp {private:double z_;public:Fp(double z = 1.0) : z_(z) {}double operator()(double p) { return z_ * p; }
};class Fq {private:double z_;public:Fq(double z = 1.0) : z_(z) {}double operator()(double q) { return z_ + q; }
};double dub(double x) { return 2.0 * x; }
double square(double x) { return x * x; }
int main() {using std::cout;using std::endl;double y = 1.21;cout << "Function pointer dub:\n";/*dub是函数名(函数名是一个指针),因此参数F对应的类型为double(*)double,即一个指向(接受一个double参数并返回一个double的)函数指针*/cout << " " << use_f(y, dub) << endl; cout << "Function pointer square:\n";cout << " " << use_f(y, square) << endl;//同上cout << "Function object Fp:\n";//Fp(5.0)是一个参数对象,F对应的类型为Fpcout << " " << use_f(y, Fp(5.0)) << endl;cout << "Function object Fq:\n";//Fq(5.0)是一个参数对象,F对应的类型为Fqcout << " " << use_f(y, Fq(5.0)) << endl;cout << "Lambda expression 1:\n";//此处是一个lambda表达式,F对应的类型为该lambda表达式使用的类型cout << " " << use_f(y, [](double u) { return u * u; }) << endl;cout << "Lambda expression 2:\n";此处是一个lambda表达式,F对应的类型为该lambda表达式使用的类型cout << " " << use_f(y, [](double u) { return u + u / 2.0; }) << endl;return 0;
}
程序通过一个模板函数use_f
使用参数f
表示调用的类型,然后将f(v)
返回。在主函数中我们6次调用模板函数,对于前两个调用的use_f
为同一个实例化。后面四个,每一个都有其对应use_f
的实例化。实际上,上述代码的运行结果如下:
Function pointer dub:use_f count = 1, &count = 0x555555756140
2.42
Function pointer square:use_f count = 2, &count = 0x555555756140
1.4641
Function object Fp:use_f count = 1, &count = 0x555555756144
6.05
Function object Fq:use_f count = 1, &count = 0x555555756148
6.21
Lambda expression 1:use_f count = 1, &count = 0x555555756138
从运行结果中,我们可以看出,在这六次调用中use_f
的实例化了5次。使用模板函数,看似统一了操作形式,但其对于不同类型的F
对模板函数都要进行一次实例化,这大大增加了编译的时长,并使头文件也增大,同时也降低了代码的执行效率。为了解决这类问题,我们首先能想到的解决办法就是:降低use_f
的实例化的次数,理想的情况下是:在这6次循环调用的时候,调用同一个use_f
的实例。针对上述例子,根据代码注释的分析,如果我们能将这6次调用中模板函数中F
的类型保持统一,就可以像第一、二次调用的情况类似,使这六次调用同一个use_f
的实例成为可能。
针对例子中的函数指针、函数对象和lambda表达式,它们有一个共同的特征:都是接受一个double
参数并返回一个double
值。也就是它们的调用特征标(它们的特征标都是double(double)
)相同。这便是function
解决这个问题的关键。【注:调用特征标是由返回类型和参数类型列表决定的,其格式为:返回类型(参数类型列表)
,其中每个参数类型用逗号分隔。】
因此,C++11引入了function
包装器。function
包装器可以简单理解为一个接口,它可以将特征标相同的函数指针、函数对象和lambda表达式等统一定义为一类特殊的对象。
function的用法
包装器function
的本质是一个模板,它是在头文件functional
中声明,其使用方法如下:
template< class >
class function; template< class R, class... Args >
class function<R(Args...)>;
其中R
为被调用函数的返回类型,Args
为被调用函数的形参。存储的可调用对象被称为std::function
的目标,如果目标为空,则调用空的function
会导致抛出std::bad_function_call
异常。
示例:
#include <functional>
#include <iostream>void print_num(int i) { std::cout << i << '\n'; }int main() {// 存储自由函数std::function<void(int)> f_display = print_num;f_display(-9);// 存储 lambdastd::function<double(double)> f_dub = [](double a) { return 2.0 * a; };std::cout << f_dub(2) << '\n';// 存储目标为空std::function<int()> f = nullptr;try {f();} catch (const std::bad_function_call& e) { //抛出异常std::cout << e.what() << '\n';}
}
输出结果为:
-9
4
bad_function_call
在了解完function
的用法之后,回到我们最开始的问题,其中,6次循环中要处理的目标的特征标均为double(double)
,因此,我们班使用function
包装器将它们将统一“包装”成function<double(double)
类型,这样模板函数use_f
将只实例化一次。使用function
包装器改进后的代码如下所示:
#include <functional>
#include <iostream>template <typename T, typename F>
T use_f(T v, F f) {static int count = 0;count++;std::cout << " use_f count = " << count << ", &count = " << &count<< std::endl;return f(v);
}class Fp {private:double z_;public:Fp(double z = 1.0) : z_(z) {}double operator()(double p) { return z_ * p; }
};class Fq {private:double z_;public:Fq(double z = 1.0) : z_(z) {}double operator()(double q) { return z_ + q; }
};double dub(double x) { return 2.0 * x; }
double square(double x) { return x * x; }
int main() {using std::cout;using std::endl;using std::function;double y = 1.21;typedef function<double(double)> fdd; // simplify the type declarationcout << "Function pointer dub:\n";cout << " " << use_f(y, fdd(dub)) << endl;cout << "Function pointer square:\n";cout << " " << use_f(y, fdd(square)) << endl;cout << "Function object Fp:\n";cout << " " << use_f(y, fdd(Fq(10.0))) << endl;cout << "Function object Fq:\n";cout << " " << use_f(y, fdd(Fp(10.0))) << endl;cout << "Lambda expression 1:\n";cout << " " << use_f(y, fdd([](double u) { return u * u; })) << endl;cout << "Lambda expression 2:\n";cout << " " << use_f<double>(y, fdd([](double u) { return u + u / 2.0; }))<< endl;return 0;
}
输出结果为:
Function pointer dub:use_f count = 1, &count = 0x555555758134
2.42
Function pointer square:use_f count = 2, &count = 0x555555758134
1.4641
Function object Fp:use_f count = 3, &count = 0x555555758134
11.21
Function object Fq:use_f count = 4, &count = 0x555555758134
12.1
Lambda expression 1:use_f count = 5, &count = 0x555555758134
1.4641
Lambda expression 2:use_f count = 6, &count = 0x555555758134
1.815
从输出结果可以看出,use_f
确实只实例化了一次,增加了编码效率,6次循环调用同一个函数,增加了代码额执行效率。
总结
function
包装器将可调用对象的类型进行统一,便于我们对其进行统一化管理,同时,使用function
包装器可以解决模板效率低下,实例化多份的问题。
参考文献
C++ Primer Plus(第六版) - 第18章 探讨C++新标准
C++ 参考手册
相关文章:
C++11 包装器function
文章首发公众号:iDoitnow C提供了多个包装器,它们主要是为了给其他编程接口提供更一致或更合适的接口。C11提供了多个包装器,这里我们重点了解一下包装器function。 对于function, C 参考手册给出的定义为: 类模板 std::function…...

XCP实战系列介绍14-基于Vector_Davinci工具的XCP配置介绍(三)
本文框架 1.概述2. 其他模块配置2.1 XCP初始化3. 手工代码部分3.1 周期函数添加3.2 DAQ Event调用3.3 XCP模块本身代码3.4 标定量的添加1.概述 在对XCP的配置部分介绍中我们计划分别对通讯部分配置、XCP模块本身配置及其他相关模块配置三篇进行介绍,在前两篇我们介绍了XCP配置…...

计算机图形学:中点BH算法对任意斜率的直线扫描转换方法
作者:非妃是公主 专栏:《计算机图形学》 博客地址:https://blog.csdn.net/myf_666 个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、问题提出二、…...

(十一)、用户中心页面【uniapp+uinicloud多用户社区博客实战项目(完整开发文档-从零到完整项目)】
1,个人中心页面 1.1 新建个人中心页面 1.2 纯净版个人中心页面代码: <template><view class"user"><view class"top"><view class"group"><view class"userinfo"><!-- 顶部 左侧 头像 …...
LA@复数和复矩阵@实对称阵相关定理
文章目录复数🎈复矩阵和复向量共轭矩阵性质定理实对称阵的相关定理复数🎈 复数 (数学) (wikipedia.org) 加法:(abi)(cdi)(ac)(bd)i)减法:(abi)−(cdi)(a−c)(b−d)i)乘法:(abi)(cdi)acbciadibdi2(ac−bd)(bcad)i除法&…...
cmd set命令笔记
使用 set是cmd最基础的命令,每个人都会用,但其实它还是有些知识的。 set 用来接收入参 set /p var请选择(1或2或3): echo %var%可以接收输入的参数。 set /p var请选择(1或2或3): echo %var% 语法 he…...

IB学校获得IBO授权究竟有多难?
IB 学校认证之路,道阻且长 The road to IB school accreditation is long and difficult一所学校能获得IB授权必须经过IBO非常严格的审核,在办学使命&教育理念、组织架构、师资力量&授课技能、学校硬件设施和课程体系上完全符合标准才可获得授权…...

火山引擎 DataTester:A/B 测试,让企业摆脱广告投放“乱烧钱”
更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 在广告投放的场景下,一线广告优化师通常会创建多个计划,去测试不同的广告素材效果。这套方法看似科学,实际上却存在诸多问题&…...

黑马redis学习记录:缓存
一、介绍 什么是缓存? 缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码 缓存无处不在 为什么要使用缓存? 因为速度快,好用缓存数据存储于代码中,而…...

CD20靶向药物|适应症|市场销售-上市药品前景分析
CD20是靶向治疗的第一个靶点,是B细胞淋巴瘤的现代治疗药物。CD20作为治疗剂的使用被认为是方便的,原因有二。首先,在 CD20 阳性肿瘤的情况下,这种受体大量存在于 B 淋巴细胞表面——每个细胞大约有十万个分子。其次,干…...
多源 复制
使复制从属服务器能够同时从多个主服务器接收事务至少需要两个主服务器和一个从属服务器设备从属服务器为每个主服务器创建一个 复制通道从属服务器必须使用基于表的资料档案库多源复制与基于文件的资料档案库不兼容不尝试检测或解决冲突如果需要此功能,则由应用程序…...

微服务项目【消息推送(RabbitMQ)】
创建消费者 第1步:基于Spring Initialzr方式创建zmall-rabbitmq消费者模块 第2步:在公共模块中添加rabbitmq相关依赖 <!--rabbitmq--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-bo…...

vr电力刀闸事故应急演练实训系统开发
电力事故是在电力生产和输电过程中可能发生的意外事件,它们可能会对人们的生命财产安全造成严重的威胁。因此,电力事故应急演练显得尤为重要。而VR技术则可以为电力事故应急演练提供一种全新的解决方案。 在虚拟环境中,元宇宙VR会模拟各种触电…...

C++类和对象补充
目录 前言: 1. 构造函数->初始化列表 1.1 初始化列表出现原因 1.2 初始化列表写法 2. explicit关键字 2.1 explict的出现 2.2 explict的写法 3. static成员 4. 友元 4.1 友元函数 4.2 友元类 5. 内部类和匿名对象 5.1 内部类 5.2 匿名对象 前言&a…...

08 SpringCloud 微服务网关Gateway组件
网关简介 大家都都知道在微服务架构中,一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢? 如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去用。 这样的架构,会存…...

极验3代 加密分析
目标链接 aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vZGVtby9zbGlkZS1mbG9hdC5odG1s接口分析 极验参数重要信息 gt和challenge;gt是固定的,但是challenge每次请求会产生不同的,这里的请求的并没有什么加密参数。 下一个请求 gettype.php,…...

python 数据分析可视化实战 超全 附完整代码数据
代码数据:https://download.csdn.net/download/qq_38735017/873799141.1 数据预处理1.1.1 异常值检测①将支付时间转为标准时间的过程中发生错误,经排查错误数据为‘2017/2/29’,后将其修改为‘2017/2/27’。②经检测发现部分订单应付金额与实付金额都为…...

有趣的HTML实例(十三) 咖啡选择(css+js)
一个人追求目标的路途是孤单的,一个人独品辛酸的时候是寂寥的,一个人马不停蹄的追赶着,狂奔着,相信前方是一片光明,我从不放弃希望,就像我对生活的信念,没有人可以动摇。 ——《北京青年》 目录…...
【力扣-LeetCode】1139. 最大的以 1 为边界的正方形 C++题解
1139. 最大的以 1 为边界的正方形难度中等137收藏分享切换为英文接收动态反馈给你一个由若干 0 和 1 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0。示例 1…...

【JavaGuide面试总结】Redis篇·下
【JavaGuide面试总结】Redis篇下1.如何使用 Redis 事务?2.如何解决 Redis 事务的缺陷?3.说说Redis bigkey吧4.大量 key 集中过期问题怎么解决的5.如何保证缓存和数据库数据的一致性?6.缓存穿透有哪些解决办法?7.缓存击穿有哪些解决…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
SQL进阶之旅 Day 22:批处理与游标优化
【SQL进阶之旅 Day 22】批处理与游标优化 文章简述(300字左右) 在数据库开发中,面对大量数据的处理任务时,单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”,深入探讨如何通过批量操作和游标技术提…...

Python异步编程:深入理解协程的原理与实践指南
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 持续学习,不断…...
uni-app学习笔记二十三--交互反馈showToast用法
showToast部分文档位于uniapp官网-->API-->界面:uni.showToast(OBJECT) | uni-app官网 uni.showToast(OBJECT) 用于显示消息提示框 OBJECT参数说明 参数类型必填说明平台差异说明titleString是提示的内容,长度与 icon 取值有关。iconString否图…...