跟我学C++中级篇——C++17标准后的std::invoke系列

一、函数调用方式

在学习编程的开始,就接触到了函数和函数的调用。可以这样讲,不会调用函数,那么最基础的语言功能便无法使用了。无论是低级到汇编或者高级到哪个语言,函数仍然是其重要的基础一环。可能随便一个学习过编程的都可会说“调用函数不是特别简单么,直接使用其函数名称就可以了,再填上相关的参数”。更或者高级一些的使用类成员函数,加个类限定符就可以了。
这都是正确的。但是,如果在某些特殊的情况下,想实现对函数的调用,该怎么办?这个特殊是指哪些?
假如有一个场景,需要依赖注入,并在注入后执行相关的函数。那么注入的可能是什么?一个普通的函数指针,一个类成员函数指针,一个lambda表达式,一个std::function或std::mem_fn等等,这都有可能。特别是在模板编程中,这种情况更是无法控制,除非使用SFINAE或者Conecpts进行控制。但如果实际的场景就是需要支持上述任意的类型呢?
有过C#开发经验的知道, 它有一个invoke方法可以调用相关的情况如委托等 。前面反复提及过,高级语言的大趋势是趋向一致的,所以C++也从C++17标准起提供了std::invoke(C++17,c++20)和std::invoke_r(c++23)两个函数模板。

二、std::invoke和std::invoke_r

先看一下定义:

std::invoke, std::invoke_rC++ Utilities library Function objects 
Defined in header <functional>
template< class F, class... Args >
std::invoke_result_t<F, Args...>invoke( F&& f, Args&&... args ) noexcept(/* see below */);   (1)	(since C++17)(constexpr since C++20)
template< class R, class F, class... Args >
constexpr Rinvoke_r( F&& f, Args&&... args ) noexcept(/* see below */);  (2)	(since C++23)1) Invoke the Callable object f with the parameters args as by INVOKE(std::forward<F>(f), std::forward<Args>(args)...). This overload participates in overload resolution only if std::is_invocable_v<F, Args...> is true.
2) Invoke the Callable object f with the parameters args as by INVOKE<R>(std::forward<F>(f), std::forward<Args>(args)...). This overload participates in overload resolution only if std::is_invocable_r_v<R, F, Args...> is true.
Parameters
f	-	Callable object to be invoked
args	-	arguments to pass to f
Return value
1) The value returned by f.
2) The value returned by f, implicitly converted to R, if R is not (possibly cv-qualified) void. None otherwise.

注意上面的constexpr。
其实很简单,就是调用一个F定义的函数对象,参数由Args…来给出。返回值是std::invoke_result_t<F, Args…>。它的定义有点吓人,但使用起来却比较方便,看下面的例子:

#include <functional>
#include <iostream>
#include <memory>void ge_func(int num) { std::cout << "call general function!value is:" << num << std::endl; }
struct ExFunc {void operator()(int num) { std::cout << "call functor!value is:" << num << std::endl; }
};
struct ExObjFunc {ExObjFunc() {}void ObjFunc(int num) { std::cout << "call object function!value is:" << num << std::endl; }
};template <typename F, typename T> void TestInvoke(F &&f, T &&t) { std::invoke(f, std::forward<T>(t)); }int main() {TestInvoke(ge_func, 1);TestInvoke(ExFunc(), 2);ExObjFunc eof;std::invoke(&ExObjFunc::ObjFunc, eof, 3);std::function<void(int)> func = std::bind(&ExObjFunc::ObjFunc, eof, std::placeholders::_1);TestInvoke(func, 3);TestInvoke([&](int num) { std::cout << "call lambda!value is:" << num << std::endl; }, 4);
}

注意,如果没有最新的环境,建议直接只使用std::invoke.
上面的代码非常简单,大家可以把自己的相关的函数对象传递进去试一下就明白了,路子都是一样的,没有什么难点。
但是这里面有一个问题,它的返回值看上去有些奇怪std::invoke_result_t ,有过相关开发经验的一眼就知道他一定是从std::invoke_result增加出来一个type类型实现的,下面的就分析一下这个返回值。

三、std::invoke_result和std::result_of

其实在C++11时就出现了std::result_of,在C++14又出现了std::result_of_t,可是在C++17基本就告诉你,别用那个了,哥给你提供了更好的std::invoke_result和std::invoke_result_t。这两个类型没别的,其实就得得到当前函数的调用结果,看名字也很容易明白。
看一下定义:

template< class >
class result_of; // not definedtemplate< class F, class... ArgTypes >
class result_of<F(ArgTypes...)>;(1)	(since C++11)(deprecated in C++17)(removed in C++20)
template< class F, class... ArgTypes >
class invoke_result; (2)	(since C++17)

它还有两个辅助类型:

template< class T >
using result_of_t = typename result_of<T>::type;(1)	(since C++14)(deprecated in C++17)(removed in C++20)
template< class F, class... ArgTypes >
using invoke_result_t = typename invoke_result<F, ArgTypes...>::type;(2)	(since C++17)

从上面的定义可以看出来,result_of在C++17就被抛弃了,到了C++20就完全被删除了。也就是说,后面只能用invoke_result,可这代码中要是有它,就苦了编码的了。
下面看一个例子:

#include <functional>
#include <iostream>int testFunc() {std::cout << "test" << std::endl;return 0;
}
int testFuncArgs(int a, int b) {std::cout << "test a+b=" << a + b << std::endl;return a + b;
}template <typename F, typename... Args> void TestArgs(F &&f, Args... args) {// static_assert(std::is_same_v<std::result_of_t<F>, int>);static_assert(std::is_same_v<std::invoke_result_t<F()>, F>);static_assert(std::is_same_v<std::invoke_result_t<F, Args...>, int>);
}
template <typename F> void Test(F &&f) {static_assert(std::is_same_v<std::result_of_t<F()>, int>);static_assert(std::is_same_v<std::invoke_result_t<F()>, F>);static_assert(std::is_same_v<std::invoke_result_t<F>, int>);
}int main() {Test(testFunc);TestArgs(testFuncArgs, 1, 2);
}

如果需要的话还可以std::decay_t, std::decay_t…> args来处理具体的类型。
结果类型在普通的编程里其实真得没什么意义,更多的应用是在模板编程和元编程中,通过结果来处理某些情况,同时利用结果的数据类型来达到某些定义使用。其实从标准的发展可以看到auto,decltype,delval等等都在不断的提供着类似的处理手段。所以重要的是灵活运用而不是拘泥于某种形式,达到目的才是重点。

四、总结

C++的变化是有目共睹的,虽然在前两年特殊情况下导致一些新技术没有被应用到想象的版本中,但在后续版本的迭代中,仍然在不断的增加和完善。C++学习的成本看似有些降低了,但实际上可能增加的成本更多了。这并不矛盾,新标准下编程可能更简单了,但背后的逻辑却更复杂了;新标准更容易理解了,但新老标准的代码混合在一起更难了。
况且,大多数的编程人员仍然使用C++98,C++11的普及都没有想象的多,更别提C++14以后的标准了。不过不要着急,慢慢来,只要坚持学习,就会达到彼岸,毕竟,学习成本比创造成本要低得多。C++新版本好歹也要三年才升级一次。

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

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

相关文章

动态规划算法学习(基础)

做题步骤&#xff1a; 确定dp数组的含义(一维或者二维) 获取递推公式 dp数组如何初始化 确定遍历顺序 打印dp数组&#xff08;检查&#xff09; 题目&#xff1a; 1. 斐波那契数 509 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 …

留子厨房开发日志

以下记录了使用go语言框架Beego&#xff0c;Mysql数据库&#xff0c;Redis数据库实现一个点菜/菜谱应用API的全过程。 技术方案 github地址 数据库设计 新建数据库&#xff1a; CREATE DATABASE menu;新建数据表&#xff1a; CREATE TABLE menu ( id int(10) unsigned NOT …

2024 CKS 题库 | 11、AppArmor

不等更新题库 CKS 题库 11、AppArmor Context: APPArmor 已在 cluster 的工作节点node02上被启用。一个 APPArmor 配置文件已存在&#xff0c;但尚未被实施。 Task: 在 cluster 的工作节点node02上&#xff0c;实施位于 /etc/apparmor.d/nginx_apparmor 的现有APPArmor 配置…

Python 实现 ADTM 指标计算:股票技术分析的利器系列(9)

Python 实现 ADTM 指标计算&#xff1a;股票技术分析的利器系列&#xff08;9&#xff09; 介绍算法解释 核心代码rolling函数介绍计算 DTMnp.where 使用介绍np.maximum 计算 DBM计算 STM计算 SBM计算 ADTM 完整代码 介绍 ADTM&#xff08;动态买卖气指标&#xff09;是一种用…

C++奇怪的 ::template

答疑解惑 怎么会有::template的写法 起初 在阅读stl的源码的时候&#xff0c;发现了一条诡异的代码 // ALIAS TEMPLATE _Rebind_alloc_t template<class _Alloc,class _Value_type> using _Rebind_alloc_t typename allocator_traits<_Alloc>::template rebind…

【misc | CTF】攻防世界 simple_transfer

天命&#xff1a;这题其实不简单啊 拿到流量包&#xff0c;丢进去wireshare&#xff0c;题目都说了flag在里面 ctrl f 直接搜索字符串 右键&#xff0c;追踪流 -> TCP流 查找 .pdf 文件&#xff0c;其实这里思路是比较奇怪的&#xff0c;毕竟是的确比较多内容&#xff0c…

基于PostGIS的慢查询引起的空间索引提升实践

目录 前言 一、问题定位 1、前端接口定位 2、后台应用定位 3、找到问题所在 二、空间索引优化 1、数据库查询 2、创建空间索引 3、geography索引 4、再看前端响应 总结 前言 这是一个真实的案例&#xff0c;也是一个新入门的工程师很容易忽略的点。往往在设计数据库的…

NestJS入门4:MySQL typeorm 增删改查

前文参考&#xff1a; NestJS入门1 NestJS入门2&#xff1a;创建模块 NestJS入门3&#xff1a;不同请求方式前后端写法 1. 安装数据库相关模块 npm install nestjs/typeorm typeorm mysql -S 2. MySql中创建数据库 ​ 3. 添加连接数据库代码 app.module.ts ​ import { M…

给自己留个备忘,blender是右手坐标系

所谓右手坐标系&#xff0c;就是三个轴的方向和右手三根手指的方向一致&#xff08;当然&#xff0c;有要求的&#xff0c;这个要求是大拇指指向x轴方向&#xff0c;食指指向y轴方向,中指指向z轴方向&#xff09;。 不过blender默认是z轴朝上的&#xff0c;如下图。 右手坐标系…

element导航菜单el-menu添加搜索功能

element导航菜单-侧栏&#xff0c;自带的功能没有搜索或者模糊查询。 找了找资料 找到一个比较可行的&#xff0c;记录一下&#xff1a; //index.vue的代码 <div style"overflow:auto"><el-menu :default-active"$route.path":default-openeds&…

<网络安全>《49 网络攻防专业课<第十三课 - 华为防火墙的使用(2)>

6 防火墙的防范技术 6.1 ARP攻击防范 攻击介绍 攻击者通过发送大量伪造的ARP请求、应答报文攻击网络设备&#xff0c;主要有ARP缓冲区溢出攻击和ARP拒绝服务攻击两种。 ARP Flood攻击&#xff08;ARP扫描攻击&#xff09;&#xff1a;攻击者利用工具扫描本网段或者跨网段主机时…

构造器详解

定义: 是一种特殊类型的方法&#xff0c;用于创建对象时初始化对象的状态。 使用new关键字创建对象 构造器特点: 1.和类名相同 2.没有返回值 public class Person {String name;public Person() {this.name"John";}}public class Test {public static void main…

vue2+element医院安全(不良)事件报告管理系统源代码

目录 安全不良事件类型 源码技术栈 医院安全&#xff08;不良&#xff09;事件报告管理系统采用无责的、自愿的填报不良事件方式&#xff0c;有效地减轻医护人员的思想压力&#xff0c;实现以事件为主要对象&#xff0c;可以自动、及时、实际地反应医院的安全、不良、近失事件…

音视频技术-网络视频会议“回声”的消除

目录 一、“回音”的成因原理 二、解决思路 三、解决方案 1、方案一 2

测试环境搭建整套大数据系统(六:搭建sqoop)

一&#xff1a;下载安装包 https://archive.apache.org/dist/sqoop/ 二&#xff1a;解压修改配置。 tar -zxvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C /opt cd /opt mv sqoop-1.4.7.bin__hadoop-2.6.0/ sqoop-1.4.7修改环境变量 vi /etc/profile#SQOOP_HOME export SQOOP_…

【K8s】-- 描述容器中 pod 的状态

命令&#xff1a;kubectl describe pod -n 你的namespace名称 pod 名称 举例&#xff1a;kubectl describe pod -n my-flink --context prod-5 test-record-all-new-mc-taskmanager-1-1 Name: test-record-all-new-mc-taskmanager-1-1 Namespace: ky-flink Pri…

Mybatis总结二

重复的类我们用工具把它包装起来&#xff1a; public class MybatisUtils {private static SqlSessionFactory factory null;static{String config "mybatis.xml";try {InputStream in Resources.getResourceAsStream(config);factory new SqlSessionFactoryBui…

nginx-------- 高性能的 Web服务端 (四)

一、高级配置 1 .1网页的状态页 基于nginx 模块 ngx_http_stub_status_module 实现&#xff0c;在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module&#xff0c;否则配置完成之后监测会是提示语法错误注意: 状态页显示的是整个服务器的状态,而非虚拟主机…

使用和管理jupyter notebook, anaconda环境下在jupyter使用自己创建的虚拟环境,怎么删除jupyter其他内核

引言&#xff1a;我有GPU&#xff0c;之前也下载了cuda&#xff0c;但是在jupyter中不能使用gpu&#xff0c;原因是我下载的cuda在另一个虚拟环境pytorch中&#xff0c;但是pytorch这个虚拟环境没有加到jupyter的内核中&#xff0c;怎么在jupyter使用自己创建的虚拟环境看二.an…

SpringCloud Gateway网关 全局过滤器[AntPathMatcher 某些路径url禁止访问] 实现用户鉴权

前提&#xff1a;先保证Gateway网关项目 和 Nacos注册中心 等可以正常访问和调用&#xff0c;搭建方法可查看博文SpringCloud Gateway网关 项目创建 及 整合Nacos开发_spring gateway如何设置工程名称-CSDN博客 类似的全局鉴权方案&#xff0c;参考如下&#xff1a; SpringClo…