首页 > 编程语言 >C++ 返回值类型推导

C++ 返回值类型推导

时间:2024-08-04 14:19:57浏览次数:10  
标签:std 推导 int C++ result 返回值 include type class

C++ 返回值类型推导

前言

C++ 中获取函数签名可以很简单地用 decltype(函数名) 来获得,但是这样无法直接提取出返回值类型。

有时我们需要获取函数或可调用对象的返回值类型,以便进行后续的操作,在泛型编程中很常用,特别是当不同的参数集的结果类型不同时。

头文件 <type_traits>

  • C++11引入了std::result_of、C++14引入了std::result_of_t

  • C++17中,废弃了std::result_of而引入了更好用的std::invoke_result和std::invoke_result_t。

std::result_of/std::result_of_t

模板类 std::result_of 是一个函数类型萃取器(function type traits),它可以推导函数类型的返回值类型。需要两个模板参数:

  • 第一个 F 是可调用类型、对函数的引用或对可调用类型的引用
  • 第二个 Args 是函数的参数类型
template< class >
class result_of; // 不定义
template< class F, class... ArgTypes >
class result_of<F(ArgTypes...)>;

示例1:

#include <type_traits>
 
int add(int x, double y)
{
    return x + static_cast<int>(y);
}
 
int main()
{
    std::result_of<decltype(add)>::value result = 0;
    static_assert(std::is_same<decltype(result), int>::value, "result type should be int");
    return 0;
}

示例2:

#include <iostream>
#include <type_traits>
 
int fn(int) {return int();}                            // function
typedef int(&fn_ref)(int);                             // function reference
typedef int(*fn_ptr)(int);                             // function pointer
struct fn_class { int operator()(int i){return i;} };  // function-like class
 
int main() {
  typedef std::result_of<decltype(fn)&(int)>::type A;  // int
  typedef std::result_of<fn_ref(int)>::type B;         // int
  typedef std::result_of<fn_ptr(int)>::type C;         // int
  typedef std::result_of<fn_class(int)>::type D;       // int
 
  std::cout << std::boolalpha;
  std::cout << "A: " << std::is_same<int,A>::value << std::endl; // true
  std::cout << "B: " << std::is_same<int,B>::value << std::endl; // true
  std::cout << "C: " << std::is_same<int,C>::value << std::endl; // true
  std::cout << "D: " << std::is_same<int,D>::value << std::endl; // true
 
  return 0;
}

一个模板中应用的实例:

有一个vector,Person就是一个简单的结构体,包含name,age,city三个字段,想要编写一个GroupBy函数,实现对这个vector按Person的某个字段分组。因为字段未定,编写一个模板比较好。思路是向GroupBy传一个函数,让用户决定这个字段。分组比较简单,数据插入一个multimap<T,Person>返回即可。但是定义multimap中的T类型由用户传入的函数决定。于是这时候就可以用result_of来确定函数的返回值,即T的类型.

#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>

using namespace std;

struct Person {
    string name;
    int age;
    string city;
};

vector<Person> vt = {
    { "aa", 20, "shanghai" },
    { "bb", 25, "beijing" },
    { "cc", 20, "nanjing" },
    { "dd", 25, "nanjing" }
};


template <typename Fn>
multimap<typename result_of<Fn(Person)>::type, Person>
GroupBy(const vector<Person>& vt, const Fn& keySelector)
{
    typedef typename result_of<Fn(Person)>::type key_type;
    multimap<key_type, Person> ret; // 利用了红黑树map的有序特性来实现分组
    for_each(vt.begin(), vt.end(),
        [&](const Person& p) {
            ret.insert(make_pair(keySelector(p), p));
        });
    return ret;
}

int main()
{
    // 按年龄分组
    auto res = GroupBy(vt, [](const Person& p) { return p.age; });
    // 按城市分组
    auto res1 = GroupBy(vt, [](const Person& p) { return p.city; });

    // 打印结果
    cout << "----------group by age:---------------" << endl;
    for_each(res.begin(), res.end(), [](decltype(res)::value_type& p) {
        cout << p.second.name << " " << p.second.city << "  " << p.second.age << endl;
    });
    cout << "----------group by city:---------------" << endl;
    for_each(res1.begin(), res1.end(), [](decltype(res1)::value_type& p) {
        cout << p.second.name << " " << p.second.city << "  " << p.second.age << endl;
    });
    return 0;
}

运行结果:

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

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

需要两个模板参数:

  • 第一个 F 是可调用类型、对函数的引用或对可调用类型的引用、成员函数指针和指向成员函数的指针,以及 std::function
  • 第二个 Args 是函数的参数类型
template< class F, class... ArgTypes >
class invoke_result; // 不定义
template< class F, class... ArgTypes >
invoke_result<F, ArgTypes...>;

示例:

#include <type_traits>
#include <functional>
class A
{
public:
    int add(int x, double y) // 成员函数
    {
        return x + static_cast<int>(y);
    }
};
 
int main()
{
    std::invoke_result<decltype(&A::add), A*, int, double>::type result = 0;
    static_assert(std::is_same<decltype(result), int>::value, "result type should be int");
    
    std::function<int(int, double)> add = [](int x, double y) {
        return x + static_cast<int>(y);
    };
    std::invoke_result<decltype(add), int, double>::type result = 0;
    static_assert(std::is_same<decltype(result), int>::value, "result type should be int");
    return 0;
}

标签:std,推导,int,C++,result,返回值,include,type,class
From: https://www.cnblogs.com/3to4/p/18341705

相关文章

  • kettle从入门到精通 第八十三课 ETL之kettle kettle调用python且接收返回值
    场景:kettle调用python执行脚本,处理之后,再把结果数据流发给下一个步骤。 看到有个qq群里有个小伙伴求助要实现kettle调用python脚本,然后接收python脚本执行的结果,最后将结果传递到下一个步骤。之前的课程里面介绍的是kettle通过shell步骤调用python脚本,没有接收python返回的结果......
  • 【C++核心篇】—— C++面向对象编程:封装相关语法使用和注意事项详解(全网最详细!!!)
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、封装(类)1.封装的使用(类和对象)2.对象的初始化和清理2.1构造函数2.2析构函数2.3构造函数的分类及调用3.深拷贝与浅拷贝4.C++对象模型和this指针5.友元6.运算符重载前言在本篇......
  • 【C++基础篇】—— 面向对象编程前的准备(内存分区,引用、函数重载)
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、内存分区模型1.C++内存分区2.new操作符二、引用三、函数重载1.函数基本使用2.函数重载前言在本篇文章中,主要是对C++的基础语法进行回顾学习,回顾学习C++的基本语法规则、数据类型......
  • CPlusPlus - #034 C++ 中的零拷贝技术
    文章目录C++中的零拷贝技术探析1前言2.1使用mmap实现零拷贝2.2使用sendfile实现零拷贝2.3优缺点和适用场合2.3.1mmap2.3.1.1优点2.3.1.2缺点2.3.1.3适用场景2.3.2sendfile2.3.2.1优点2.3.2.2缺点2.3.2.3适用场景2.4总结......
  • 【C++ STL】vector
    文章目录vector1.vector的接口1.1默认成员函数1.2容量操作1.3访问操作1.4修改操作1.5vector与常见的数据结构的对比2.vector的模拟实现2.1类的定义2.2默认成员函数迭代器的分类2.3容量接口memcpy浅拷贝问题内存增长机制reserve和resize的区别2.4修改接口......
  • 基于OpenCV C++的网络实时视频流传输——Windows下使用TCP/IP编程原理
    1.TCP/IP编程1.1概念IP是英文InternetProtocol(网络之间互连的协议)的缩写,也就是为计算机网络相互连接进行通信而设计的协议。任一系统,只要遵守IP协议就可以与因特网互连互通。所谓IP地址就是给每个遵循tcp/ip协议连接在Internet上的主机分配的一个32bit地址。按照TC......
  • 【leetcode详解】另一棵树的子树 (C++递归:思路精析&& 过程反思)
    思路详解:总体框架:对root树进行先序遍历,如果当前结点(记为cur)的值和subRoot的根节点值相等时,就开始判断 以cur为根节点的树和子树是否结构一样?如何判断两棵树是否结构完全相同?分析:一提到“树”结构,很容易想到在(先/中/后序)遍历上做文章,请教了AI后笔者得知,如果两棵树......
  • c++中的迭代器
    前言hello大家好,我是文宇。正文C++中的迭代器是一种访问容器中元素的对象。它可以看作是一种抽象的指针,通过迭代器可以便捷地遍历和操作容器中的元素,无需了解容器内部的数据结构和实现细节。迭代器提供了一组操作,包括指向容器中的元素、移动到下一个元素、访问当前元素等功......
  • 【每日一题】【并查集】【力扣】695.岛屿的最大面积 C++
    力扣695.岛屿的最大面积695.岛屿的最大面积题目描述给你一个大小为m×nm\timesnm×n的二进制矩阵......
  • C++ //练习 16.27 对下面每条带标签的语句,解释发生了什么样的实例化(如果有的话)。如果
    C++Primer(第5版)练习16.27练习16.27对下面每条带标签的语句,解释发生了什么样的实例化(如果有的话)。如果一个模板被实例化,解释为什么;如果未实例化,解释为什么没有。template<typenameT>classStack{};voidf1(Stack<char>); //(a)classExercise{ Stack<dou......