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

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

文章首发公众号&#xff1a;iDoitnow C提供了多个包装器&#xff0c;它们主要是为了给其他编程接口提供更一致或更合适的接口。C11提供了多个包装器&#xff0c;这里我们重点了解一下包装器function。 对于function, C 参考手册给出的定义为&#xff1a; 类模板 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算法对任意斜率的直线扫描转换方法

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机图形学》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、问题提出二、…...

(十一)、用户中心页面【uniapp+uinicloud多用户社区博客实战项目(完整开发文档-从零到完整项目)】

1,个人中心页面 1.1 新建个人中心页面 1.2 纯净版个人中心页面代码&#xff1a; <template><view class"user"><view class"top"><view class"group"><view class"userinfo"><!-- 顶部 左侧 头像 …...

LA@复数和复矩阵@实对称阵相关定理

文章目录复数&#x1f388;复矩阵和复向量共轭矩阵性质定理实对称阵的相关定理复数&#x1f388; 复数 (数学) (wikipedia.org) 加法&#xff1a;(abi)(cdi)(ac)(bd)i)减法&#xff1a;(abi)−(cdi)(a−c)(b−d)i)乘法&#xff1a;(abi)(cdi)acbciadibdi2(ac−bd)(bcad)i除法&…...

cmd set命令笔记

使用 set是cmd最基础的命令&#xff0c;每个人都会用&#xff0c;但其实它还是有些知识的。 set 用来接收入参 set /p var请选择&#xff08;1或2或3&#xff09;: echo %var%可以接收输入的参数。 set /p var请选择&#xff08;1或2或3&#xff09;: echo %var% 语法 he…...

IB学校获得IBO授权究竟有多难?

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

火山引擎 DataTester:A/B 测试,让企业摆脱广告投放“乱烧钱”

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 在广告投放的场景下&#xff0c;一线广告优化师通常会创建多个计划&#xff0c;去测试不同的广告素材效果。这套方法看似科学&#xff0c;实际上却存在诸多问题&…...

黑马redis学习记录:缓存

一、介绍 什么是缓存&#xff1f; 缓存(Cache)&#xff0c;就是数据交换的缓冲区&#xff0c;俗称的缓存就是缓冲区内的数据&#xff0c;一般从数据库中获取&#xff0c;存储于本地代码 缓存无处不在 为什么要使用缓存&#xff1f; 因为速度快,好用缓存数据存储于代码中,而…...

CD20靶向药物|适应症|市场销售-上市药品前景分析

CD20是靶向治疗的第一个靶点&#xff0c;是B细胞淋巴瘤的现代治疗药物。CD20作为治疗剂的使用被认为是方便的&#xff0c;原因有二。首先&#xff0c;在 CD20 阳性肿瘤的情况下&#xff0c;这种受体大量存在于 B 淋巴细胞表面——每个细胞大约有十万个分子。其次&#xff0c;干…...

多源 复制

使复制从属服务器能够同时从多个主服务器接收事务至少需要两个主服务器和一个从属服务器设备从属服务器为每个主服务器创建一个 复制通道从属服务器必须使用基于表的资料档案库多源复制与基于文件的资料档案库不兼容不尝试检测或解决冲突如果需要此功能&#xff0c;则由应用程序…...

微服务项目【消息推送(RabbitMQ)】

创建消费者 第1步&#xff1a;基于Spring Initialzr方式创建zmall-rabbitmq消费者模块 第2步&#xff1a;在公共模块中添加rabbitmq相关依赖 <!--rabbitmq--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-bo…...

vr电力刀闸事故应急演练实训系统开发

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

C++类和对象补充

目录 前言&#xff1a; 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组件

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

极验3代 加密分析

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

python 数据分析可视化实战 超全 附完整代码数据

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

有趣的HTML实例(十三) 咖啡选择(css+js)

一个人追求目标的路途是孤单的&#xff0c;一个人独品辛酸的时候是寂寥的&#xff0c;一个人马不停蹄的追赶着&#xff0c;狂奔着&#xff0c;相信前方是一片光明&#xff0c;我从不放弃希望&#xff0c;就像我对生活的信念&#xff0c;没有人可以动摇。 ——《北京青年》 目录…...

【力扣-LeetCode】1139. 最大的以 1 为边界的正方形 C++题解

1139. 最大的以 1 为边界的正方形难度中等137收藏分享切换为英文接收动态反馈给你一个由若干 0 和 1 组成的二维网格 grid&#xff0c;请你找出边界全部由 1 组成的最大 正方形 子网格&#xff0c;并返回该子网格中的元素数量。如果不存在&#xff0c;则返回 0。示例 1&#xf…...

【JavaGuide面试总结】Redis篇·下

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

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

FFmpeg avformat_open_input函数分析

函数内部的总体流程如下&#xff1a; avformat_open_input 精简后的代码如下&#xff1a; int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...