1. 简介
在C++中,有时我们需要获取函数或可调用对象的返回值类型,以便进行后续的操作,在泛型编程中很常用,特别是当不同的参数集的结果类型不同时。在早期的C++版本中,我们需要手动推导函数返回值类型,这个过程非常复杂,也容易出错。为了解决这个问题,C++11引入了std::result_of
和std::result_of_t
(C++14),这两个模板可以方便地获取函数或可调用对象的返回值类型。而在C++17中,废弃了std::result_of
而引入了更好用的std::invoke_result
和std::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_of
和std::invoke_result
是两个非常有用的工具,可以方便地获取函数、成员函数和可调用对象的返回值类型。它们可以避免手动推导函数返回值类型的复杂过程,减少错误和代码量。如果你需要获取函数、成员函数或可调用对象的返回值类型,建议使用std::result_of_t
或std::invoke_result_t
来实现。
5. Reference
- std::result_of, std::invoke_result
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)