【C++11 —— 包装器】

news2024/11/19 7:48:29

C++11 —— 包装器

  • 包装器
    • function包装器
      • function包装器介绍
      • function包装器统一类型
      • function包装器的意义
    • bind包装器
      • bind包装器介绍
      • bind包装器绑定固定参数
      • bind包装器调整传参顺序
      • bind包装器的意义

包装器

function包装器

function包装器介绍

function包装器 也叫作适配器。C++ 中的 function 本质是一个类模板,也是一个包装器。这些包装器主要用于提供一致且适合的接口以便于处理各种可调用对象,如函数、函数对象、lambda表达式等。

std::function的原型如下:

template< class R, class... Args >
class function<R(Args...)>;

其中:

  • R: 是被调用函数的返回类型。
  • Args: 是被调用函数的参数类型列表。
std::function<int(int, int)> func; // func 可以存储接受两个 int 参数并返回 int 的可调用对象

function包装器统一类型

std::function是一个类模板,它提供了一种统一的方式来存储和调用各种可调用对象,包括:

  • 普通函数
  • Lambda 表达式
  • 函数指针
  • 函数对象(重载了 operator() 的类)
  • 成员函数指针
  • 数据成员指针

通过指定模板参数,std::function可以处理具有特定返回类型和参数类型列表的可调用对象。它使用模板转换构造函数来接收被包装的函数对象。

下面这段代码,useF是一个函数模板,接受两个模板参数FT
F表示可调用对象的类型,T表示参数的类型
使用可调用对象f,传入参数x,同时声明静态整形变量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;
	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
	return 0;
}

在这里插入图片描述
可以看到这里的count都是1,并且这里的地址也都不相同,说明这里的useF函数模板被实例化了三份。

但其实这里没必要实例化三份出来,虽然传入的可调用对象的类型不相同,但是外层显示看到的参数和返回值都是相同的,此时就可以使用包装器来进行上层封装,来优化效率,只实例化一个模板。

int main()
{
	// 函数名
	cout << useF(f, 11.11) << endl;
	// 函数对象
	cout << useF(Functor(), 11.11) << endl;
	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;

	cout << endl << endl;
	function<double(double)> func1 = f;
	cout << useF(func1, 11.11) << endl;

	function<double(double)> func2 = Functor();
	cout << useF(func2, 11.11) << endl;

	function<double(double)> func3 = [](double d)->double {return d / 4; };
	cout << useF(func3, 11.11) << endl;

	return 0;
}

进行包装后可见,打印出来的count的每次地址都是相同的,并且count被累加到3,说明function做到了类型的统一,并且让其调用了3次。
在这里插入图片描述

使用 std::function的一些注意事项:

  1. 转换后的 std::function 对象的参数类型必须能转换为可调用对象的参数类型。
  2. 可调用对象的返回类型必须能转换为 std::function 对象的返回类型。
  3. 如果尝试调用一个存储了空可调用对象的 std::function,会抛出 std::bad_function_call 异常。

function包装器的意义

C++中存在多种可调用对象,包括普通函数、仿函数和lambda表达式。使用包装器(如std::function)可以将这些不同的可调用对象统一为一种类型,从而简化调用和管理。
比如下面的代码,在调用的时候就会显得比较凌乱,不统一,不符合C++11的编程习惯:


//包装器
#include <functional>

int f(int a, int b)
{
	return a + b;
}

struct F
{
	int operator()(int a,int b)
	{
		return a + b;
	}
};


int main()
{
	cout << f(1, 2) << endl;
	cout << "------------------" << endl;

	F f1;
	cout << f1(2, 3) << endl;
	cout << "------------------" << endl;

	cout << F()(3, 4) << endl;
	cout << "------------------" << endl;

	cout << [](int a, int b) {return a + b; }(4, 5) << endl;
	cout << "------------------" << endl;

	return 0;
}

在这里插入图片描述

所以使用包装器,可以做到接口的统一化,同时function也同时提供了类型安全的接口。通过定义返回类型和参数类型,编译器能够在编译的时候检查类型一致性,避免运行时错误。
包装器也减少了模板参数化的数量,从而提高了编译效率,通过将相同特征标的可用调用对象统一为一个类型,避免了多次实例化的问题!

	// 使用 std::function 包装普通函数 f
	function<int(int, int)> func1 = f; // func1 是一个可调用对象,类型为 int(int, int)
	cout << func1(1, 2) << endl; // 调用 func1,输出 3(1 + 2)
	cout << "------------------" << endl;

	// 使用 std::function 包装临时的 F 对象
	function<int(int, int)> func2 = F(); // 创建 F 的临时对象并包装
	cout << func2(2, 3) << endl; // 调用 func2,输出 5(2 + 3)
	cout << "------------------" << endl;

	// 创建 F 的实例 f1,并使用 std::function 包装它
	F f1; // 实例化 F
	function<int(int, int)> func3 = f1; // 将 f1 包装到 func3 中
	cout << func3(3, 4) << endl; // 调用 func3,输出 7(3 + 4)
	cout << "------------------" << endl;

	// 使用 std::function 包装 lambda 表达式
	function<int(int, int)> func4 = [](int a, int b) { return a + b; }; // lambda 表达式
	cout << func4(4, 5) << endl; // 调用 func4,输出 9(4 + 5)
	cout << "------------------" << endl;

在这里插入图片描述

bind包装器

bind包装器介绍

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

bind函数模板的原型如下:

template<typename F, typename... Args>
auto bind(F&& f, Args&&... args);

模板参数说明:

  • F 是可调用对象的类型。
  • Args 是绑定的参数类型,可以是值或占位符。

bind包装器绑定固定参数

可以使用 std::bind 将某些参数固定,从而生成一个新的可调用对象。例如:

#include <iostream>
#include <functional>

void printSum(int a, int b, int c) {
    std::cout << "Sum: " << (a + b + c) << std::endl;
}

int main() {
    // 绑定固定参数
    auto boundFunc = std::bind(printSum, 1, 2, std::placeholders::_1);
    
    // 调用时只需要提供最后一个参数
    boundFunc(3); // 输出: Sum: 6
    return 0;
}

在这里插入图片描述
在这个示例中,printSum 函数的前两个参数被固定为 1 和 2,而第三个参数使用占位符 _1,在调用boundFunc时提供。

bind包装器调整传参顺序

std::bind 还可以通过占位符调整参数的顺序。例如:

#include <iostream>
#include <functional>

void printValues(int a, int b, int c) {
    std::cout << "Values: " << a << ", " << b << ", " << c << std::endl;
}

int main() {
    // 调整参数顺序
    auto boundFunc = std::bind(printValues, std::placeholders::_2, 42, std::placeholders::_1);
    
    // 调用时提供两个参数
    boundFunc(10, 20); // 输出: Values: 20, 42, 10
    return 0;
}

在这里插入图片描述
在上面的代码中,printValues 函数的参数顺序被调整,第二个参数固定为 42,而 _1 和 _2 分别对应调用时提供的第一个和第二个参数。

bind包装器的意义

std::bind 是 C++11 中的一个函数适配器,它允许将可调用对象与部分参数绑定,从而生成新的可调用对象,简化函数调用并提高代码可读性。通过绑定固定参数和调整参数顺序,std::bind 提供了灵活性,使得在回调函数和事件处理等场景中,开发者能够更方便地管理和使用各种可调用对象。

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

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

相关文章

【Go】使用Goland创建第一个Go项目

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

go-zero的快速实战(完整)

微服务框架 go-zero 的基本介绍 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性&#xff0c;经受了充分的实战检验。 go-zero 中的 api&#xff0c;rpc&#xff0c;数据库等涉及的代码&#xff0c;都可以给我们一键生成&#…

计算机毕业设计 自习室座位预约系统的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

RTC、ADC

RTC RTC&#xff08;Real-Time Clock&#xff09;是实时时钟模块&#xff0c;用于跟踪实际时间&#xff08;年、月、日、时、分、秒&#xff09;&#xff0c;即使在系统断电或处于低功耗模式下也能保持时间的准确性。 特点 时间和日期跟踪低功耗模式支持可编程闹钟和定时器备…

贪心算法day31|56. 合并区间、738. 单调递增的数字(整数与字符串的转换)、贪心刷题总结

贪心算法day31|56. 合并区间、738. 单调递增的数字、贪心刷题总结 56. 合并区间738. 单调递增的数字贪心刷题总结 56. 合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 …

大模型微调 - 自动加载预训练模型

大模型微调 - 自动加载预训练模型 flyfish AutoModelFor 是 Hugging Face transformers 库中的一个通用接口&#xff0c;这里用的是modelscope &#xff0c;用于自动加载预训练模型&#xff0c;涵盖多种任务的模型。AutoModelFor 后面接不同的任务名称会对应不同的模型架构&a…

基于 CycleGAN 对抗网络的自定义数据集训练

目录 生成对抗网络&#xff08;GAN&#xff09; CycleGAN模型训练 训练数据生成 下载开源项目CycleGAN 配置训练环境 开始训练 模型测试 可视化结果 生成对抗网络&#xff08;GAN&#xff09; 首先介绍一下什么是GAN网络&#xff0c;它是由生成器&#xff08;Generator…

工具、环境等其他小问题归纳

此篇文章内容会不定期更新&#xff0c;仅作为学习过程中的笔记记录 一、查询Windows 10环境下python版本与安装路径 若电脑成功安装了python环境&#xff0c;不小心忘了版本。 I、查询版本 1、cmd窗口快捷查询 Win R 输入cmd 进入窗口&#xff1b; 直接输入 python --version …

2024.9.13 系统运维

学习目标&#xff1a;了解 云计算运维 “云计算是中国的骄傲&#xff01;” 企业向云服务商租用云服务&#xff08;省钱、省心、省力&#xff09; 云计算&#xff1a;公有云、私有云&#xff08;大公司&#xff0c;数据隐私性&#xff09;、混合云&#xff08;私有云跑重要…

前端刷新进不了登录页面

报错props.ts:15 Uncaught (in promise) SyntaxError: Unexpected token 错误截图&#xff1a; 原因&#xff1a;谷歌浏览器版本过低&#xff0c;升级浏览器 比如这边版本就过低了

ThinkCMF框架任意内容包含漏洞的讲解

本文来自无问社区&#xff0c;更多网安资料可前往查看http://www.wwlib.cn 背景描述 ThinkCMF是一款基于PHPMYSQL开发的中文内容管理框架&#xff0c;底层采用ThinkPHP3.2.3构建。 ThinkCMF提出灵活的应用机制&#xff0c;框架自身提供基础的管理功能&#xff0c;而开发者可…

CSP 2023 提高级第一轮单项选择题解析

CSP 2023 提高级第一轮单项选择题解析 第1题第2题第3题第4题第5题第6题第7题第8题第9题第10题第11题第12题第13题第14题第15题 第1题 在 Linux 系统终端中&#xff0c;以下哪个命令用于创建一个新的目录&#xff1f;(B) A.newdir B.mkdir C.create D.mkfold 解析&#xff1a;记…

部署Tomcat和抓包

部署Tomcat 复制文件到桌面 查看自己是否有java环境&#xff0c;下图所示是有的&#xff0c;若没有需另行下载 解压tomcat文件 tar -xzvf apache-tomcat-7.0.96.tar.gz 下列为tomcat文件的几个重要文件 进入到bin文件中 启动tomcat ./startup.sh 可以先用本机查看是否启动…

【PostgreSQL里的restartpoint重启点】

不知道大家有没有关注过&#xff0c;配置文件里archive_cleanup_command参数的注释部分有着这么一句"command to execute at every restartpoint",意思是在每个restartpoint时执行的命令。 提起checkpoint大家可能比较熟悉&#xff0c;对于这个restartpoint&#xff…

英文软件汉化中文软件教程asi exe dll 等汉化教程

相信大家在使用国际软件的时候&#xff0c;会经常碰到英文类型的软件 或者玩一些游戏使用一些工具&#xff0c;也基本都是外网的&#xff0c;那么对于用户来讲 就会非常的不方便&#xff01; 小编为大家整理了一些国内大佬出的的英文软件汉化中文软件的视频教程 教程分为EX…

HarmonyOS开发实战( Beta5.0)滑动视频自动播放案例实践

鸿蒙HarmonyOS开发往期文章必看&#xff1a; HarmonyOS NEXT应用开发性能实践总结 最新版&#xff01;“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&#xff01;&#xff08;从零基础入门到精通&#xff09; 介绍 本示例主要介绍视频列表滑动到屏幕中间自动播放场景&…

[项目] -登录框

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天来给大家讲解登录框的小练习&#xff0c;就此SDK的相关学习就此结束 登录框 对话框绘制 通过添加DIaLog对话框&#xff0c;添加 static test文本、Edit Control输入框、Button按钮&#xff0c;制作登录框passwor…

快速入门编写一个Java程序

一、jdk配置 下载完jdk后需要配置环境变量 以下是其步骤 1、我的电脑-属性-高级系统设置-环境变量 2、在系统变量中新建JAVA_HOME环境变量&#xff0c;指向jdk的安装目录 3、编辑path环境变量&#xff0c;新建%JAVA_HOME%\bin 4、打开Dos命令行&#xff0c;任意目录下敲入j…

CGAL and the Boost Graph Library

CGAL and the Boost Graph Library 许多几何数据结构都可以解释为图&#xff0c;因为它们由顶点和边组成。对于halfedge数据结构、多面体曲面、arrangement以及二维三角剖分类来说&#xff0c;情况都是如此。利用对偶性&#xff0c;人们也可以将面解释为顶点&#xff0c;相邻面…

AcWing119 袭击

目录 AcWing119 袭击题目描述背景输入输出数据范围 题解解法优化 打赏 AcWing119 袭击 题目描述 背景 特工进入据点突袭发电站&#xff0c;已知所有发电站的位置和所有特工的降落位置&#xff0c;求任意特工距离任意核电站的最短距离 输入 第一行一个整数 T T T&#xff0…