std::invoke与自己实现模板比较

一、自定义模板函数

在前面分析了自定义一个模板函数,用来实现类似JAVA探针的形式。但是在文末给了一个小问题,就是如果这个模板参数是一个类成员函数该怎么办?本来不急于想做这个分析,但后来在看STL中的std::invoke的源码时,发现二者有相通之处,便结合其源码,一起分析一下。
那么如果Fn是一个类成员函数应该怎么处理呢?这种解决方式有两种,一种是使用刚刚提到的std::invoke的内部处理方式;另外一种就可以通过使用std::function来绑定(std::bind)一个类成员函数,然后 再调用自定义模板函数即可。

二、std::invoke

先看一下std::invoke的源码实现:

 namespace detail
{template<class>constexpr bool is_reference_wrapper_v = false;template<class U>constexpr bool is_reference_wrapper_v<std::reference_wrapper<U>> = true;template<class C, class Pointed, class T1, class... Args>constexpr decltype(auto) invoke_memptr(Pointed C::* f, T1&& t1, Args&&... args){if constexpr (std::is_function_v<Pointed>){if constexpr (std::is_base_of_v<C, std::decay_t<T1>>)return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);else if constexpr (is_reference_wrapper_v<std::decay_t<T1>>)return (t1.get().*f)(std::forward<Args>(args)...);elsereturn ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);}else{static_assert(std::is_object_v<Pointed> && sizeof...(args) == 0);if constexpr (std::is_base_of_v<C, std::decay_t<T1>>)return std::forward<T1>(t1).*f;else if constexpr (is_reference_wrapper_v<std::decay_t<T1>>)return t1.get().*f;elsereturn (*std::forward<T1>(t1)).*f;}}
} // namespace detailtemplate<class F, class... Args>
constexpr std::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)noexcept(std::is_nothrow_invocable_v<F, Args...>)
{if constexpr (std::is_member_pointer_v<std::decay_t<F>>)return detail::invoke_memptr(f, std::forward<Args>(args)...);elsereturn std::forward<F>(f)(std::forward<Args>(args)...);
}

这段代码其实是从STL的实现中进行了精简的,在STL中,__invoke_impl的版本有好几个,涉及到普通函数、成员函数及其它几种相关的调用。
重点来看一下其对类成员函数的处理:

      if constexpr (std::is_base_of_v<C, std::decay_t<T1>>)return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);else if constexpr (is_reference_wrapper_v<std::decay_t<T1>>)return (t1.get().*f)(std::forward<Args>(args)...);elsereturn ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);

首先判断是否是C的子类(本身也算子类),如果不是则判断是否为T1的is_reference_wrapper_v对象,最后如果都不是直接转发。回头再看看自己定义的模板,是不是少很多呢。这个其实不重要,毕竟又不是写库。重点是对类成员变量函数的处理std::is_member_pointer_v及相关的几个元编程定义的相关接口。

三、自定义模板函数的绑定处理

这里仍然用前面的例子,前面用std::bind的方式来处理一下类内部成员函数:

template <typename Fn, typename... Args> void TestTime1(Fn &f, Args... args) {f(std::forward<Args>(args)...);
}
int main() {std::function<void(int)> fn =std::bind(&Ex::GetData, ex, std::placeholders::_1);TestTime1(fn, 1);return 0;
}

其实,使用std::bind和std::function来处理一下Fn和std::invoke的处理机制,基本相似。但是这样会显得有点不通用,所以可以借用一下std::invoke的实现机制来处理一下自定义的模板函数:

template <class C, class Pointed, class T1, class... Args>
constexpr decltype(auto) invoke_memptr(Pointed C::*f, T1 &&t1, Args &&...args) {if constexpr (std::is_function_v<Pointed>) {if constexpr (std::is_base_of_v<C, std::decay_t<T1>>)return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);}
}
template <typename Fn, typename... Args> void TestTime1(Fn &&f, Args... args) {// f(std::forward<Args>(args)...);if constexpr (std::is_member_pointer_v<std::decay_t<Fn>>)  //1return invoke_memptr(f, std::forward<Args>(args)...);else  //2return std::forward<Fn>(f)(std::forward<Args>(args)...);
}class Ex {
public:void GetData(int t) { std::cout << "this value:" << t + 1 << std::endl; }
};Ex ex;void GetTestData(int a, int b) {std::cout << "a,b value is:" << a << " " << b << std::endl;
}
int main() {
//call 2std::function<void(int)> fn =std::bind(&Ex::GetData, ex, std::placeholders::_1);TestTime1(fn, 1);
//call 2TestTime1(GetTestData, 'a', '1');
//call 1 ==>invoke_memptrTestTime1(&Ex::GetData, ex, 3);return 0;
}

这样,就基本可用了。更丰富的迭代可以根据自己的需要不断的添加和改写。

四、总结

要学会站在别人的肩膀上前进。遇到问题,除了要关于思考,更要关于借助外力。最重要的是,要把外力整合为内力。上学的时候不是学过,“君子性非异也,善假于物也”。共勉!

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

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

相关文章

前端 -- 基础 网页、HTML、 WEB标准 扫盲详解

什么是网页 : 网页是构成网站的基本元素&#xff0c;它通常由 图片、链接、文字、声音、视频等元素组成。 通常我们看到的网页 &#xff0c;常见以 .html 或 .htm 后缀结尾的文件&#xff0c; 因此俗称 HTML 文件 什么是 HTML : HTML 指的是 超文本标记语言&#xff0c…

Python入门--变量和数据类型

什么是变量&#xff1f; 在编程中&#xff0c;变量是指内存中的一段存储空间&#xff0c;用于存储数据。使用变量可以方便地存储数据并在程序中进行操作。 如何定义变量&#xff1f; 在Python中&#xff0c;可以使用“”符号来定义变量&#xff0c;例如&#xff1a; a 1 b …

基于IMX6ULLmini的linux裸机开发系列七:中断处理流程

中断上下文 cpu通过内核寄存器来运行指令并进行数据的读写处理的&#xff0c;它在进入中断前一个时刻的具体值&#xff0c;称为中断上下文 中断上下文是指CPU在进入中断之前保存的寄存器状态和其他相关信息。当CPU接收到中断请求时&#xff0c;它会保存当前正在执行的指令的状…

[python爬虫] 爬取图片无法打开或已损坏的简单探讨

本文主要针对python使用urlretrieve或urlopen下载百度、搜狗、googto&#xff08;谷歌镜像&#xff09;等图片时&#xff0c;出现"无法打开图片或已损坏"的问题&#xff0c;作者对它进行简单的探讨。同时&#xff0c;作者将进一步帮你巩固selenium自动化操作和urllib…

sklearn机器学习库(二)sklearn中的随机森林

sklearn机器学习库(二)sklearn中的随机森林 集成算法会考虑多个评估器的建模结果&#xff0c;汇总之后得到一个综合的结果&#xff0c;以此来获取比单个模型更好的回归或分类表现。 多个模型集成成为的模型叫做集成评估器&#xff08;ensemble estimator&#xff09;&#xf…

CloudCompare——统计滤波

目录 1.统计滤波2.软件实现3.完整操作4.算法源码5.相关代码 本文由CSDN点云侠原创&#xff0c;CloudCompare——统计滤波&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫。 1.统计滤波 算法原理见&#xff1a;PCL 统计滤波器…

基于IMX6ULLmini的linux裸机开发系列八:按键处理实验

目录 GIC相关寄存器 GPIO中断相关寄存器 中断服务函数表 中断向量表偏移位置 make有报错 解决方法&#xff1a;error: for loop initial declarations are only allowed in C99 mode_‘for’ loop initial declarations are only allowed i_Young_2717的博客-CSDN博客 GIC…

智慧工地一体化云平台源码:监管端、工地端、危大工程、智慧大屏、物联网、塔机、吊钩、升降机

智慧工地解决方案依托计算机技术、物联网、云计算、大数据、人工智能、VR&AR等技术相结合&#xff0c;为工程项目管理提供先进技术手段&#xff0c;构建工地现场智能监控和控制体系&#xff0c;弥补传统方法在监管中的缺陷&#xff0c;最终实现项目对人、机、料、法、环的全…

linux RabbitMQ-3.8.5 安装

软件版本操作系统CentOS Linux release 7.9.2009erlangerlang-23.0.2-1.el7.x86_64rabbitMQrabbitmq-server-3.8.5-1.el7 RabbitMQ的安装首先需要安装Erlang,因为它是基于Erlang的VM运行的。 RabbitMQ安装需要依赖:socat和logrotate&#xff0c;logrotate操作系统已经存在了&…

【Redis】什么是缓存穿透,如何预防缓存穿透?

【Redis】什么是缓存穿透&#xff0c;如何预防缓存穿透&#xff1f; 缓存穿透是指查询一个一定不存在的数据&#xff0c;由于缓存中不存在&#xff0c;这时会去数据库查询查不到数据则不写入缓存&#xff0c;这将导致这个不存在的数据每次请求都要到数据库去查询&#xff0c;这…

图像检索技术研究:深度度量与深度散列在相似性学习中的应用比较与实践 - 使用Python与Jupyter环境

引言 在计算机视觉领域&#xff0c;图像检索是一个长期存在并持续受到研究者关注的重要话题。随着大数据时代的到来&#xff0c;如何高效、准确地从海量数据中检索到相似的图像成为一个巨大的挑战。传统的检索方法在大数据环境下表现不佳&#xff0c;而深度学习技术的崛起为图…

Redis中常见的缓存穿透、缓存击穿、缓存雪崩、缓存预热解决方案

文章目录 一、缓存穿透1. 什么是缓存穿透2. 解决方案2.1 无效的key存放到Redis2.2 引入布隆过滤器2.3 如何选择&#xff1a; 二、缓存击穿1. 什么是缓存击穿2. 解决方案 三、缓存雪崩1. 什么是缓存雪崩2. 解决方案2.1 均匀过期2.2 热点数据缓存永远不过期2.3 采取限流降级的策略…

零基础如何学习 Web 安全,如何让普通人快速入门网络安全?

前言 网络安全现在是朝阳行业&#xff0c;缺口是很大。不过网络安全行业就是需要技术很多的人达不到企业要求才导致人才缺口大 【一一帮助安全学习&#xff08;网络安全面试题学习路线视频教程工具&#xff09;一一】 初级的现在有很多的运维人员转网络安全&#xff0c;初级…

Opencv 图像的读取与写入

目录 导入cv2 读取图像数据 创建一个窗口 waitKey方法 关闭所有窗口 完整示例 保存图片 示例 导入cv2 # 导入opencv包 import cv2 读取图像数据 cv2.imread(path, flag) 参数说明&#xff1a; path&#xff1a;要读取的图像文件的路径。 flag&#xff08;可选&#…

Linux0.11内核源码解析-truncate.c

truncate文件只要实现释放指定i节点在设备上占用的所有逻辑块&#xff0c;包括直接块、一次间接块、二次间接块。从而将文件节点对应的文件长度截为0&#xff0c;并释放占用的设备空间。 索引节点的逻辑块连接方式 释放一次间接块 static void free_ind(int dev,int block) {…

听GPT 讲Prometheus源代码--rules

Prometheus的rules目录主要包含规则引擎和管理规则的文件: engine.go 该文件定义了规则引擎的接口和主要结构,包括Rule,Record,RuleGroup等。它提供了规则的加载、匹配、评估和结果记录的功能。 api.go 定义了用于管理和查询规则的RESTful API,包括获取、添加、删除规则等方法。…

机器学习深度学习——BERT(来自transformer的双向编码器表示)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——transformer&#xff08;机器翻译的再实现&#xff09; &#x1f4da;订阅专栏&#xff1a;机器学习&am…

《Go 语言第一课》课程学习笔记(八)

基本数据类型 Go 原生支持的数值类型有哪些&#xff1f; Go 语言的类型大体可分为基本数据类型、复合数据类型和接口类型这三种。 其中&#xff0c;我们日常 Go 编码中使用最多的就是基本数据类型&#xff0c;而基本数据类型中使用占比最大的又是数值类型。 整型 Go 语言的…

理解二分查找的单调性与局部舍弃性

二分是一种很有用的优化途径&#xff0c;因为它能够导致减半运算&#xff0c;而对于能否二分&#xff0c;有一个界定标准&#xff1a;状态的决策过程或者序列是否满足单调性或者局部舍弃性。序列指的是像数组这些能够用下标索引表示的容器。单调性指的是&#xff0c;各个元素能…

学习笔记:Opencv实现限制对比度得自适应直方图均衡CLAHE

2023.8.19 为了完成深度学习的进阶&#xff0c;得学习学习传统算法拓展知识面&#xff0c;记录自己的学习心得 CLAHE百科&#xff1a; 一种限制对比度自适应直方图均衡化方法&#xff0c;采用了限制直方图分布的方法和加速的插值方法 clahe&#xff08;限制对比度自适应直方图…