C++ std::result_of/std::invoke_result的简明指南

2023-05-16

1. 简介

在C++中,有时我们需要获取函数或可调用对象的返回值类型,以便进行后续的操作,在泛型编程中很常用,特别是当不同的参数集的结果类型不同时。在早期的C++版本中,我们需要手动推导函数返回值类型,这个过程非常复杂,也容易出错。为了解决这个问题,C++11引入了std::result_ofstd::result_of_t(C++14),这两个模板可以方便地获取函数或可调用对象的返回值类型。而在C++17中,废弃了std::result_of而引入了更好用的std::invoke_resultstd::invoke_result_t。下面则先介绍std::result_of再介绍std::invodke_result

2. std::result_of/std::result_of_t

std::result_of是一个函数类型萃取器(function type traits),它可以推导函数类型的返回值类型,它定义在头文件<type_traits>中。std::result_of模板类需要两个模板参数:第一个是函数类型,第二个是函数的参数类型。它的定义如下:

template <typename F, typename... Args>
class result_of<F(Args...)>;

在模板参数中,F必须是可调用类型、对函数的引用或对可调用类型的引用Args代表函数参数类型。例如,如果我们有一个函数add,它的类型为int(int, double),我们可以按照下列示例代码来使用std::result_of以获取其返回值类型:

#include <type_traits>

int add(int x, double y)
{
    return x + static_cast<int>(y);
}

int main()
{
    std::result_of<int(int, double)>::type result = 0;
    static_assert(std::is_same<decltype(result), int>::value, "result type should be int");
    return 0;
}

在这个示例中,我们使用std::result_of<int(int, double)>::type来获取返回值类型,并使用std::is_same来检查返回值类型是否为int。这里,因为我们知道函数的入口(或理解为函数指针),所以std::result_of<int(int, double)>::type也可以改成std::result_of<decltype(add)>::type。而代码中的result是一个变量,而不是类型,所以不能直接在std::is_same中引用,即不能写成std::is_same<result, int>,必须使用decltype获取result的类型。

C++14引入了一个方便的类型别名std::result_of_t,它可以替代std::result_of<F(Args...)>::type,简化代码。使用std::result_of_t,上面的示例可以写成:

#include <type_traits>

int add(int x, double y)
{
    return x + static_cast<int>(y);
}

int main()
{
    std::result_of_t<decltype(add)> result = 0;
    static_assert(std::is_same<decltype(result), int>::value, "result type should be int");
    return 0;
}

3. std::invoke_result/std::invoke_result_t

在C++17中,std::result_of已被弃用,建议使用std::invoke_result来代替。std::invoke_result可以获取函数、成员函数和可调用对象的返回值类型。与std::result_of不同的是,std::invoke_result支持成员函数指针指向成员函数的指针,以及可调用对象的包装器std::function

std::invoke_result/std::invoke_result_t的定义如下:

template <typename F, typename... Args>
struct invoke_result;

template <typename F, typename... Args>
using invoke_result_t = typename invoke_result<F, Args...>::type;

在模板参数中,F代表函数类型成员函数指针类型可调用对象类型Args代表函数或成员函数的参数类型。例如,如果我们有一个函数add,它的类型为int(int, double),我们可以参照如下示例代码使用std::invoke_result_t来获取它的返回值类型:

#include <type_traits>

int add(int x, double y)
{
    return x + static_cast<int>(y);
}

int main()
{
    std::invoke_result_t<decltype(add), int, double> result = 0;
    static_assert(std::is_same<decltype(result), int>::value, "result type should be int");
    return 0;
}

在这个示例中,我们使用std::invoke_result_t<decltype(add), int, double>来获取函数add的返回值类型,并使用std::is_same来检查返回值类型是否为int

如果我们有一个类A和一个成员函数A::add(),则可以使用下列代码获取成员函数的返回值:

#include <type_traits>

class A
{
public:
    int add(int x, double y)
    {
        return x + static_cast<int>(y);
    }
};

int main()
{
    std::invoke_result_t<decltype(&A::add), A*, int, double> result = 0;
    static_assert(std::is_same<decltype(result), int>::value, "result type should be int");
    return 0;
}

在这个示例中,我们使用std::invoke_result_t<decltype(&A::add), A*, int, double>来获取成员函数A::add的返回值类型。需要注意的是,由于成员函数A::add()是属于类的,而成员函数必须通过类的对象或指针进行调用,因此需要将函数的第一个参数类型指定为该函数所在类的类型的指针类型,即"A*"。(编译器为我们隐藏了调用成员函数时所需要的this指针参数,所以这里会显得有点绕)。剩下类型就是该成员函数的其他参数类型。

如果我们有一个可调用对象,我们可以直接将它传递给std::invoke_result_t。例如:

#include <type_traits>
#include <functional>

int main()
{
    std::function<int(int, double)> add = [](int x, double y) {
        return x + static_cast<int>(y);
    };
    std::invoke_result_t<decltype(add), int, double> result = 0;
    static_assert(std::is_same<decltype(result), int>::value, "result type should be int");
    return 0;
}

我们直接将可调用对象add传递给std::invoke_result即可。

需要注意的是,如果函数、成员函数或可调用对象不接受指定的参数类型,则编译时将会出现错误。

4. 总结

综上所述,std::result_ofstd::invoke_result是两个非常有用的工具,可以方便地获取函数、成员函数和可调用对象的返回值类型。它们可以避免手动推导函数返回值类型的复杂过程,减少错误和代码量。如果你需要获取函数、成员函数或可调用对象的返回值类型,建议使用std::result_of_tstd::invoke_result_t来实现。

5. Reference

  1. std::result_of, std::invoke_result
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ std::result_of/std::invoke_result的简明指南 的相关文章

  • C语言字符数组的输入和输出

    字符数组的输入输出有两种方法 xff1a xff08 1 xff09 逐个字符输入输出 用格式符 c 输入或输出一个字符 例如 span class token keyword int span span class token funct
  • android手机开启IPv6(电信)

    安卓手机开启IPv6 xff08 电信 xff09 系统设置找到移动网络 接入点 接入点选择CTNET 接入设置点进去可以找到APN协议 xff0c 选择IPv4 IPv6即可 实测手机这样设置后开热点笔记本 xff0c 能稳定获得ipv6
  • C语言 怎样定义函数

    1 定义函数 C语言要求 xff0c 在程序中用到的所有函数必须要 先定义 xff0c 后使用 定义函数应包括以下几个内容 xff1a xff08 1 xff09 指定函数的名字 xff0c 以便以后按名调用 xff08 2 xff09 指
  • C语言 函数的返回值

    通过函数调用使主调函数能得到一个确定的值 xff0c 这就是函数值 xff08 函数的返回值 xff09 1 函数的返回值是通过函数中的return语句获得的 return语句将被调用函数中的一个确定值带回到主调函数中去 如果需要从被调函数
  • C语言 函数的嵌套调用

    C语言的函数定义是互相平行 独立的 xff0c 也就是说 xff0c 在定义函数时 xff0c 一个函数内不能再定义另一个函数 xff0c 即不能嵌套定义 xff0c 但可以嵌套调用函数 xff0c 即 xff0c 在调用一个函数的过程中
  • C语言 Hanoi(汉诺)塔问题,用递归解决

    问题 古代有一个梵塔 xff0c 塔内有3个座A xff0c B xff0c C 开始时A座上有64个盘子 xff0c 盘子大小不等 xff0c 大的在下 xff0c 小的在上 有一个老和尚想把64个盘子从A作移到C座 xff0c 但规定每
  • C语言 数组元素的指针

    1 一个变量有地址 xff0c 一个数组包含若干个元素 xff0c 每个数组元素都在内存中占用存储单元 xff0c 它们都有相应的地址 2 指针变量既然可以指向变量 xff0c 当然也可以指向数组元素 xff08 把某一元素的地址放入一个指
  • Python 实验三 控制语句

    1 从键盘接收整数的一百分制成绩 xff08 0到100 xff0c 要求输出其对应的成绩等级A E 其中 xff0c 90分 xff08 包含 xff09 以上为A xff0c 80 89 xff08 均包含 xff09 分为B xff0
  • Python 实验四 常用数据结构(1)

    1 从键盘输入一个正整数列表 xff0c 以一1结束 xff0c 分别计算列表中奇数和偶数的和 n span class token operator 61 span span class token builtin int span sp
  • Python 实验四 常用数据结构(2)

    6 某企业为职工发放奖金 xff1a 如果入职超过5年 xff0c 且销售业绩超过15000元的员工 xff0c 奖金比例为0 2 xff1b 销售业绩超过10000元的员工 xff0c 奖金比例为0 15 xff1b 销售业绩超过5000
  • JavaScript之入门4篇(DOM节点操作)

    学习记录 xff1a 学习视频链接https www bilibili com video BV1Sy4y1C7ha p 61 194 amp spm id from 61 pageDriver 1 节点操作 xff1a 1 利用DOM提供
  • Please wait while Jenkins is getting ready to work (jenkins)

    如果界面提示Jenkins正在启动 请稍后 或者提示 Please wait while Jenkins is getting ready to work 解决方法 1 需要你进入jenkins的工作目录 xff0c 打开 hudson m
  • 异常点检测的应用场景与检测方法(含代码实操案例)

    异常点检测概述 这里常说的异常点 xff0c 目前并没有具体的严格定义 xff0c 大多数来讲 xff0c 检测异常点都是按照数据分布与业务逻辑结合进行主观判断是否属于异常点 关注金科应用研院 xff0c 回复 CSDN xff0c 领取风
  • ApacheDS搭建

    前提 安装jdk sudo apt get install openjdk 8 jdk 安装 先安装apacheds2 配置客户端远程登录 xff0c 这里使用Apache Directory Studio xff0c 配置界面如下 xff
  • Windows 10 内置linux执行带GUI的应用程序

    1 安装MobaXterm xff0c 并运行 2 打开内置的Linux xff0c 命令执行带GUI的运行程序即可
  • Repo介绍

    目录 1 概要2 工作原理 2 1 项目清单库 repo manifests 2 2 repo脚本库 repo repo 2 3 仓库目录和工作目录 3 使用介绍 3 1 init3 2 sync3 3 upload3 4 download
  • Android 8.0.0-r4源码目录结构详解

    android的移植按如下流程 1 android linux 内核的普通驱动移植 让内核可以在目标平台上运行起来 2 正确挂载文件系统 确保内核启动参数和 android 源代码 system core rootdir 目录下的 init
  • Android8.0.0-r4的编译系统

    一 概述 1 1 编译系统变化 从Android 7 0开始 xff0c android的编译系统发生了变化 xff0c 之前依赖Makefile组织编译系统 xff0c 从7 0开始逐步引入了kati soong optional未正式使
  • [Android Studio]Android Studio 三种添加插件的方式

    何给Android Studio添加插件 添加插件的路径有三种 xff0c 我把他们分类如下 xff1a 点击设置小按钮 点击 xff3b Plugins xff3d 这里展示的是你已经安装的插件 xff0c 我们可以点击插件名称 xff0
  • Gerrit 服务器插件安装-示例插件delete project

    gerrit2 X 中没法直接删除一个项目 xff0c 之前需要手工删除 xff0c 后来社区提供了一个插件delete project来搞定这个事 xff0c 安装方法如下 xff1a 到 gerritforge xff0c 找到对应的

随机推荐

  • Windows平台下载Android源码(整理)

    Google官方下载源码使用的系统Ubuntu系统 xff0c 不过现在我们需要在Windows系统中下载Android源码文件 网站的地址是 xff1a https android googlesource com 里面包括Android
  • Ubuntu 16.04 文件服务器--samba的安装和配置

    Samba是在Linux系统上实现的SMB xff08 Server Messages Block xff0c 信息服务块 xff09 协议的一款免费软件 它实现在局域网内共享文件和打印机 xff0c 是一个客户机 服务器型协议 客户机通过
  • 深入剖析Android音频之AudioTrack

    播放声音能够用MediaPlayer和AudioTrack xff0c 两者都提供了java API供应用开发人员使用 尽管都能够播放声音 但两者还是有非常大的差别的 当中最大的差别是MediaPlayer能够播放多种格式的声音文件 比如M
  • 树莓派4 运行 Tensorflow Lite

    树莓派4 运行 Tensorflow Lite 1 更新树莓派 span class token function sudo span apt update 2 下载安装脚本 span class token function git sp
  • 操作系统进程进行系统调用详细过程

    翻阅很多资料 xff0c 综合了各处所述进程在进行系统调用之后的状态会如何的解答 xff0c 以下是我个人理解 xff0c 欢迎各位读者纠错 PS 特别感谢以下这个帖子 xff0c 看完他们的讨论我才茅塞顿开 xff0c 非常感谢 xff0
  • 解决Ubuntu 找不到ARM64 的源的问题(转)

    Ubuntu 安装了NVIDIA的驱动还有DriveWokrs之后 xff0c 好像把系统添加了arm64的架构 xff0c 因此 xff0c 在源更新的时候 xff0c 也会更新arm64相关的源 xff0c 但是问题在于 xff0c 用
  • asp.net 实现打开文件所在的文件夹, 本地可以打开,发布后点击按钮没有反应的解决办法

    此类情况大概是安全范畴的问题 确定上传文件夹的共享 xff0c iis 以及电脑帐户 xff0c 以及aspnet 等是否有对应的相关权限 1 确认ASPNET 账户属于管理员级别 2 在 服务 里面找到 IIS Admin xff0c 双
  • numpy 和 tensor 的区别

    关系 xff1a 两者共享内存 xff0c 转换方便 xff0c 没有额外的开销 区别 xff1a 1 数据类型上面的区别 xff1a numpy 默认类型是 float64 int32 tensor 默认类型是float32 int64
  • 关于docker无法apt-get update的问题

    在看这篇文章https www jianshu com p 21d66ca6115e 有一个部分是 但是发现自己的 Node 没有ping命令 想着去apt get update 但是出现如下错误 只要在命令签名加上 sudo 就行
  • ubuntu下安装zip unzip

    安装命令 apt get install zip unzip 执行命令常见错误 xff1a 1 unable to locate package 解决办法 xff1a 执行sudo apt get update命令后再执行安装命令就可以了
  • 平衡小车卡尔曼滤波算法

    最近研究STM32的自平衡小车 xff0c 发现有两座必过的大山 xff0c 一为卡尔曼滤波 xff0c 二为PID算法 网上看了很多关于卡尔曼滤波的代码 xff0c 感觉写得真不咋地 一怒之下 xff0c 自己重写 xff0c 不废话 x
  • FreeRTOS学习-前言与FreeRTOS发行版

    1 前言 因为工作的需要 xff0c 学习FreeRTOS已经有一段时间了 接下来一段时间会定期更新本人学习FreeRTOS的系列笔记 系列笔记主要参考了官方的说明手册和FreeRTOS的源代码 其主要思想是先了解FreeRTOS的对外接口
  • FreeRTOS学习-内存管理

    1 动态内存分配与FreeRTOS 从v9 0 0后 xff0c FreeRTOS开始支持内核对象的静态分配方式 xff0c 因此 xff0c 内存管理库可以被裁剪 但在大多数嵌入式应用中 xff0c 堆的使用还是非常常见的 因此 xff0
  • FreeRTOS学习-任务管理(Task管理)(1)

    1 简介 任务管理 xff08 或称进程管理 xff09 是所有操作系统内核的最基本组成模块之一 xff0c FreeRTOS也不例外 想要了解一个操作系统 xff0c 不得不理解其任务管理的设计和实现 任务管理的介绍由两篇文章组成 xff
  • Java基础之Java枚举

    絮叨 昨天刚好有遇到一个枚举的小问题 xff0c 然后发现自己并不是那么熟悉它 xff0c 然后在开发中 xff0c 枚举用的特别多 xff0c 所以有了今天的文章 什么是枚举 Java中的枚举是一种类型 xff0c 顾名思义 xff1a
  • C++ STL 移动一个vector的元素到另一个vector

    1 背景 有的时候 xff0c 我们需要提取某个现有的vector中的元素到另一个vector中 xff0c 或者对多维的vector进行纬度的转换 在这种场景下 xff0c 往往原始的vector中的数据可能并不需要了 xff0c 为了节
  • Qt/C++ 临时屏蔽控件信号(signal)的实用方法

    1 背景 在使用Qt的控件时 xff0c 我们大概率会使用Qt的信号与槽 xff08 signal slot xff09 的机制来实现自己的UI交互逻辑 由于Qt内置控件的信号种类是有限的 xff0c 我们常常会遇到如下窘境 xff1a 以
  • FreeRTOS学习-队列管理

    1 简介 在FreeRTOS中 xff0c 提供了多种任务间通讯的机制 xff0c 包括消息队列 信号量和互斥锁 事件组 任务通知 xff0c 他们的总体特征如下图所示 xff1a 从图中可以看出 xff0c 消息队列 信号量和互斥锁 事件
  • Qt/C++ 如何删除QListWidget的指定项

    1 简介 QListWidget是Qt中 xff0c 用于展示列表类型数据的常用控件 它提供了一个类似于QListView的列表视图 xff0c 但是具有用于添加和删除项的接口 QListWidget使用一个内部模型来管理列表中的每个QLi
  • C++ std::result_of/std::invoke_result的简明指南

    1 简介 在C 43 43 中 xff0c 有时我们需要获取函数或可调用对象的返回值类型 xff0c 以便进行后续的操作 xff0c 在泛型编程中很常用 xff0c 特别是当不同的参数集的结果类型不同时 在早期的C 43 43 版本中 xf