【模板进阶】std::function

news2024/11/17 20:46:13

一、 s t d : : f u n c t i o n std::function std::function的介绍

s t d : : f u n c t i o n std::function std::function C + + 11 C++11 C++11引入的一个可调用对象包装器,它可以通过指定模板参数,统一来处理各自可调用对象。

二、实现类似 s t d : : f u n c t i o n std::function std::function的功能

这里,我们一步步演示如何自定义一个类似 s t d : : f u n t i o n std::funtion std::funtion的类模板。

首先,我们声明一个泛化版本和特化版本的类:

//泛化版本
template<typename T>
class CallFuncObj; //用不到,声明即可

//特化版本

template<typename T,typename... Args>
class CallFuncObj<T(Args...)> {

};

因为泛化版本用不到,所以我们简单声明即可,重点是在特化版本上。


为了让 C a l l F u n c O b j CallFuncObj CallFuncObj能够接受任意的可调用对象作为实参,那么我们有必要为它实现一个构造函数模板,如下:

//构造函数模板,用于接受各种可调用对象
template<typename U>
CallFuncObj(U&& acobj);

这里使用万能引用,无论接受的是左值还是右值,都没有问题。


包装了可调用对象,我们还需要一个类能够调用它,因此我们引入一个新的类模板 C F O b j H a n d l e r CFObjHandler CFObjHandler,作为 C a l l F u n c O b j CallFuncObj CallFuncObj类的成员类型,它是一个指针,指向这个类:

template<typename T,typename... Args>
class CallFuncObj<T(Args...)> {
private:
	CFObjHandler<T, Args...>* handler; //可调用对象处理器指针,父类指针
};

紧接着,我们要实现这个类。
在标准库中 s t d : : f u n c t i o n std::function std::function是通过虚函数来实现对可调用对象的调用的,因此我们也增加一个虚函数,在子类中实现它。

//添加可调用对象处理器
template<typename T,typename ...Args>
class CFObjHandler {
public:
	//纯虚函数,后续要创建子类要实现它
	virtual T invoke(Args... args) const = 0 ;

};

我们新增一个子类$CFObjHandlerChild $,然后继承 C F O b j H a n d l e r CFObjHandler CFObjHandler类。
实现一下它的构造函数和 i n v o k e invoke invoke虚函数:


//实现CFObjHandler的子类,用于接收在allFuncObj中构造函数的U&&类型参数
template<typename U,typename T,typename... Args>
class CFObjHandlerChild :public CFObjHandler<T, Args...> {
private:
	U functor; //U是一个可调用对象,这里保存可调用对象的一个副本

public:
	CFObjHandlerChild(U &&funcobj):functor(std::forward<U>(funcobj)) {} //完美转发到初始化列表,保持信息完整性
	
	//重写一下虚函数
	virtual T invoke(Args... args) const {
		return functor(std::forward<Args>(args)...); //完美转发
	}

};

注意这里构造函数用的也是万能引用,然后通过完美转发来实现构造。


最后,我们完善一下 C a l l F u n c O b j CallFuncObj CallFuncObj类,补充一下构造函数:

CallFuncObj(U&& acobj) { //注意这里是右值引用,才能是接收所有的参数类型
	//初始化对象处理器指针,父类指针指向指针
	handler = new CFObjHandlerChild<U, T, Args...>(std::forward<U>(acobj)); //完美转发
}

注意这里是用父类指针指向子类对象,由于用到万能引用,这里我们也是使用完美转发。


我们还需要重载以下 ( ) () ()运算符,让 C a l l F u n c O b j CallFuncObj CallFuncObj具备类似函数的功能。

具体而言就是直接使用 h a n d l e r handler handler来调用子类 C F O b j H a n d l e r C h i l d CFObjHandlerChild CFObjHandlerChild i n v o k e invoke invoke函数。

//重载operator()
T operator()(Args...args) const {
	//调用可调用对象
	return handler->invoke(std::forward<Args>(args)...); //使用完美转发,不丢失信息
}


然后别忘了补充一下析构函数,回收一下指针内存:

//析构函数
~CallFuncObj() {
	delete handler;
}

完整代码如下:

//创建一个简单的函数包装器

//前向声明
template<typename T,typename... Args>
class CFObjHandler;

template<typename U, typename T, typename... Args>
class CFObjHandlerChild;

//泛化版本
template<typename T>
class CallFuncObj; //用不到,声明即可

//特化版本

template<typename T,typename... Args>
class CallFuncObj<T(Args...)> {
private:
	CFObjHandler<T, Args...>* handler; //可调用对象处理器指针,父类指针

public:

	//构造函数模板,用于接受各种可调用对象
	template<typename U>
	CallFuncObj(U&& acobj) { //注意这里是右值引用,才能是接收所有的参数类型
		//初始化对象处理器指针,父类指针指向指针
		handler = new CFObjHandlerChild<U, T, Args...>(std::forward<U>(acobj)); //完美转发
	}

	//重载operator()
	T operator()(Args...args) const {
		//调用可调用对象
		return handler->invoke(std::forward<Args>(args)...); //使用完美转发,不丢失信息
	}

	//析构函数
	~CallFuncObj() {
		delete handler;
	}


};

//添加可调用对象处理器

template<typename T,typename ...Args>
class CFObjHandler {

public:
	//虚函数,后续要创建子类
	virtual T invoke(Args... args) const {
		return T{};
	};

};


//实现CFObjHandler的子类,用于接收在allFuncObj中构造函数的U&&类型参数
template<typename U,typename T,typename... Args>
class CFObjHandlerChild :public CFObjHandler<T, Args...> {
private:
	U functor; //U是一个可调用对象,这里保存可调用对象的一个副本

public:
	CFObjHandlerChild(U &&funcobj):functor(std::forward<U>(funcobj)) {} //完美转发到初始化列表,保持信息完整性
	
	//重写一下虚函数
	virtual T invoke(Args... args) const {
		return functor(std::forward<Args>(args)...); //完美转发
	}

};


三、自定义函数包装器的测试

简单测试一下,这里包装三类可调用对象: l a m b a d a lambada lambada表达式、全局函数、仿函数:


//全局函数
void myfunc1(int tmprv) {
	std::cout << "调用了myfunc1!\n";
	std::cout << "tmprv = " << tmprv << "\n";
}

//仿函数
struct myfunc2 {
	void operator()(int x, double y) const { //需要添加const修饰符,因为包装器内的虚函数invoke是const类型的
		std::cout << "调用了myfunc2!\n";
	}
};
//包装匿名函数

auto add = [](int x, int y) {
	std::cout << "调用了add函数!\n";
	return x + y; 
	};

测试函数:

void Test() {
	//包装全局函数
	CallFuncObj<void(int)>f1 = myfunc1;
	f1(100);

	CallFuncObj<int(int, int)>f2 = add;

	int res = f2(1, 2);
	std::cout << res << "\n";

	//包装仿函数
	CallFuncObj<void(int, double)> f3 = myfunc2{};
	f3(1, 2.0);
}

测试结果如下,都能够正确运行:
在这里插入图片描述

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

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

相关文章

食家巷小程序:品味平凉美食,领略甘肃风情

甘肃平凉犹如一颗璀璨的明珠&#xff0c;散发着独特的魅力。这里不仅有壮丽的自然风光&#xff0c;更有令人垂涎欲滴的美食。而食家巷小程序&#xff0c;就如同一个神奇的美食宝库&#xff0c;将平凉的美味呈现在你的指尖。 &#x1f4a5;平凉&#xff0c;美食的天堂。 平凉红…

erlang学习:Linux命令学习11

crontab命令 crontab命令是用于管理定时任务的命令行工具。它提供了多种选项和参数&#xff0c;可以用来创建、编辑、查看和删除用户的定时任务。 常用命令 以下是一些常用的crontab命令&#xff1a; crontab -e&#xff1a;编辑当前用户的定时任务列表。该命令会在默认编辑…

高产胜母猪,带你上线我的新项目!

大家好&#xff0c;我是程序员鱼皮。9月&#xff0c;我处于极度爆肝状态&#xff0c;成功完结了最新带大家做的项目 面试刷题平台 。 当我们做完一个项目后&#xff0c;一定要记得把项目上线&#xff0c;这样才算是完成了学习的 “闭环”&#xff0c;写到简历上也才有竞争力。 …

鲁班到家上门安装维修系统源码开发之结构功能解析

随着物联网和智能家居的普及&#xff0c;消费者对便捷、高效的生活方式需求日益增加。鲁班到家作为一款专注于家居安装维修服务的平台&#xff0c;凭借其多渠道预约、智能派单、在线支付与费用明细透明等优势&#xff0c;在市场上赢得了广泛认可。本文将详细解析鲁班到家上门安…

flash-attention代码逻辑

setup.py&#xff1a;python项目中&#xff0c;setup.py用于管理项目的构建、打包和分发过程。这个文件通常包含项目的元数据以及如何构建和安装模块的指令 三个相关命令 构建扩展模块&#xff1a;python setup.py build_ext清理构建文件&#xff1a;python setup.py clean安装…

扩展欧几里得算法 C++

题一 扩展欧几里得算法 解题思路 原链接&#xff1a;https://www.acwing.com/solution/content/1393/&#xff08;下同&#xff09; 代码实现 #include<iostream>using namespace std;void exgcd(int a, int b, int &x, int &y) {if(b 0){x 1, y 0;return ;…

Linux学习/复习5进程间通信

ctrl s 保存到云服务器 ctrl ~ 调用调试

vim插件ctags

ctags简介 ctags&#xff08;Generate tag files for source code&#xff09;是一个用来生成代码标签文件的工具。它可以为多种编程语言的源代码生成标签文件&#xff0c;这些标签文件包含了源代码中的各种元素的信息&#xff0c;如函数名、变量名、类名等。Vim 可以利用这些…

vue中css作用域及深度作用选择器的用法

Vue中有作用域的CSS 当< style>标签有scoped属性时&#xff0c;它的css只作用于当前组建中的元素。vue2和vue3均有此用法&#xff1b; 当使用scoped后&#xff0c;父组件的样式将不会渗透到子组件中。不过一个子组件的根节点会同时受父组件有作用域的css和子组件有作用…

10.9学习

1.数据库和Redis的一致性 全量缓存保证高效读取 所有数据都存储在缓存里&#xff0c;读服务在查询时不会再降级到数据库里&#xff0c;所有的请求都完全依赖缓存。此时&#xff0c;因降级到数据库导致的毛刺问题就解决了。但全量缓存并没有解决更新时的分布式事务问题&#xf…

保姆级教程 | Adobe Illustrator调整颜色透明度

背景 由于课题需要&#xff0c;现需要在Adobe Illustrator里修改部分色块的颜色及透明度 步骤 1. 打开Adobe Illustrator软件&#xff0c;打开或创建一个AI文件&#xff1a; 2. 绘制一个色块&#xff1a; 3. 单击需要调整透明度的对象将其选中 4. 调整颜色的透明度&#xf…

AD中显示Selection includes locked items.Continue?

因为框选拖动组里包含锁定的元器件&#xff0c;所以出现如下提示&#xff1b; 可以单击锁定元器件&#xff0c;点击location的锁形标记即可显示解锁的状态。 解锁之后&#xff1a; 然后就可以拖动框选的元器件了。

C/C++语言的函数指针

C语言的一个函数指针示例 #include<stdio.h> typedef int(*Calc)(int a, int b); int Add(int a, int b) {int result a b;return result; } int Sub(int a, int b) {int result a - b;return result; } int main() {int x 100;int y 200;int z 0;// 原本C语言的写…

鸿蒙Next开发速成(持续更新)

整体概念 1、API涵盖应用框架、系统、媒体、图形、应用服务、AI六大领域。 应用框架相关Kit开放能力&#xff1a;Ability Kit&#xff08;程序框架服务&#xff09;、ArkUI&#xff08;方舟UI框架&#xff09;等。系统相关Kit开放能力&#xff1a;Universal Keystore Kit&…

专利申请成功后这些问题不能忽视

专利申请成功&#xff0c;无疑是创新者的一大成就&#xff0c;但这并非终点&#xff0c;而是一个新的开始。接下来&#xff0c;一系列的问题等待着我们去关注和应对&#xff0c;以确保专利的价值得以充分实现和持续保护。 专利申请成功后&#xff0c;需要注意以下几个重要问题&…

招联金融2025秋招倒计时

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…

自由学习记录(4)

网站域名的分级 而一级区域就是我们花钱钱买的 从一级区域bilibili开始就可以访问到对应的服务器了 儿子永远是儿子 我们既然可以免费得到人家的大域名给的儿子&#xff0c;那我们是不是在小域名里乱来&#xff0c;影响大服务器的服务&#xff1f;没这回事&#xff0c;虽然人…

PHP+MySQL组合开发的智能名片小程序源码系统 带完整的安装代码包以及搭建部署教程

系统概述 该系统采用PHP作为后端开发语言&#xff0c;MySQL作为数据库管理系统&#xff0c;通过Web技术实现数据的存储、处理和展示。前端则采用小程序框架&#xff0c;提供用户友好的交互界面。系统支持名片信息的快速录入、智能分类、搜索与分享&#xff0c;还支持名片数据的…

货币政策工具

本文为个人学习笔记&#xff0c;内容源于教材&#xff1b;整理记录的同时也作为一种分享。 1. 简介 货币政策工具作为央行实现货币政策目标的经济手段&#xff0c;以期达到最终目标&#xff0c;即物价稳定&#xff0c;充分就业&#xff0c;经济增长&#xff0c;国际收支平衡。…

高效开发最佳实践全面指南

学会表达 在写复杂表达式时&#xff0c;可使用一个变量将表达式用变量的方式表示函数、变量命名要语义化 学会复盘 花一些时间清理自己的代码尽量以函数式进行编程 拥抱变化 在开发功能时&#xff0c;要考虑变化的情况。该死的产品经理 在封装时要考虑能否封装成一个 js 模…