c++之std::declval 和 decltype以及std::result_of,invoke_result

https://juejin.cn/post/7021306822350864414std::declval 和 decltype
https://www.jianshu.com/p/6606796de366c++11: std::declval/std::decltype
https://www.cnblogs.com/heartchord/p/5039894.htmlstd::decay
https://www.apiref.com/cpp-zh/cpp/utility/functional/invoke.htmlstd::invoke
https://en.cppreference.com/w/cpp/types/result_ofstd::result_of, std::invoke_result
https://stackoverflow.com/questions/47875365/correct-way-of-using-invoke-resultCorrect way of using invoke_result?

std::decltype

decltype(expr) 是一个 C++11 新增的关键字,它的作用是将实体或者表达式的类型求出来。其最重要的点在于能不经过运算,直接推导出类型,但是其问题在于如果类对象,必须要成功构造。

auto a = value;
decltype(exp) b = value; //exp需要可运行

同时,其可以配合auto来进行类型推导,例如
auto test_decltype2(Fn f, Args… args) -> decltype(f(args…)){
与std::declval的区别如下,declval需要把每个参数和fn转成引用,其可跳过构造
decltype(std::declval< Fn>()(std::declval< Args>()…)) test_decltype1(Fn f,Args… args){

std::declval

declval将T转换成引用类型,配合decltype使用,跳过构造函数使用成员函数

定义于头文件 <utility>
template<class T>
typename std::add_rvalue_reference<T>::type declval() noexcept;  //配合decltype 表达式使用

将任意类型 T 转换成引用类型,令在 decltype 表达式中不必经过构造函数就能使用成员函数。
通常在模板中使用 declval ,模板接受的模板实参通常可能无构造函数,但有同一成员函数,均返回所需类型。
注意, declval 只能用于不求值语境,且不要求有定义;求值包含此函数的表达式是错误。通常,若 odr 使用此函数被则程序为病式。

注意引用折叠

std::add_lvalue_reference<T&>::type 是 T&
std::add_lvalue_reference<T&&>::type 是 T&
std::add_rvalue_reference<T&>::type 是 T&
std::add_rvalue_reference<T&&>::type 是 T&&

std::result_of, std::invoke_result

Defined in header <type_traits>
template< class >
class result_of; // not defined

template< 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;
只有当F可调用时,才返回type

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;

result_of与invoke_result的区别在于使用方式,
std::result_of_t<Fn(Args…)> test_decltype3(Fn f, Args… args) {
std::invoke_result_t<Fn,Args…> test_decltype4(Fn f, Args… args) {

具体代码分析

#include<utility>
#include<iostream>
#include <functional>

#include <type_traits>

int test(int a,int b){
    int c = a+b;
    std::cout<<"Run Test"<<c<<std::endl;
    return c;
}

class Kyoe
{
  public:
  Kyoe(int a){std::cout<<"construct Kyoe"<<std::endl;}
  int fun(){std::cout<<"fun Kyoe"<<std::endl;return 1;};
};
class Tyoe
{
  public:
  Tyoe(){std::cout<<"construct Tyoe"<<std::endl;}
  int fun(){std::cout<<"fun Tyoe"<<std::endl;return 1;};
};

class Jyoe
{
  public:
  Jyoe(const Jyoe& jy){std::cout<<"copy construct"<<std::endl;}  //创建拷贝构造函数后,编译器不会自动创建默认构造函数
    int fun(){std::cout<<"fun Jyoe"<<std::endl;return 1;};
};
/* 无法通过编译
template<typename Fn, typename... Args>
decltype(Fn(Args...)) test_decltype3(Fn f, Args... args) {
    std::cout<<"Run Test_Decltype3"<<std::endl;
    auto res = f(args...);
    return res;
}
*/

template<typename Fn, typename... Args>
std::result_of_t<Fn(Args...)> test_decltype3(Fn f, Args... args) {
    std::cout<<"Run Test_Decltype3"<<std::endl;
    auto res = f(args...);
    return res;
}

template<typename Fn, typename... Args>
std::invoke_result_t<Fn,Args...> test_decltype4(Fn f, Args... args) {
    std::cout<<"Run Test_Decltype4"<<std::endl;
    auto res = f(args...);
    return res;
}

template<typename Fn, typename... Args>
auto test_decltype2(Fn f, Args... args) -> decltype(f(args...)){ //这里必须这么使用,因为decltype需要实例化,但是f需要在入参中先声明,而declval则不需要,可以写成临时变量类型
    std::cout<<"Run Test_Decltype2"<<std::endl;
    auto res = f(args...);
    return res;
}

template<typename Fn,typename... Args>
decltype(std::declval<Fn>()(std::declval<Args>()...)) test_decltype1(Fn f,Args... args){
    std::cout<<"Run Test_Decltype1"<<std::endl;
    auto res = f(args...);
    return res;
}


int main(int agrc,char *argv[]){

    std::invoke(test,1,2);   //这里正常invoke执行函数
    decltype(std::invoke(test,1,2)) d; //这里是获取invoke的返回值类型,并未运行test函数,因为decltype不需要计算表达式,直接推导
    decltype(Tyoe().fun()) n = 1;  //这里同上,并未运行fun,但是必须要可构造并执行
    //decltype(Jyoe().fun()) m = 1;  //这里失败,因为没有默认构造函数,导致创建失败
    decltype(std::declval<Jyoe>().fun()) m = 1;  //这里可以成功,declval可以不经过构造函数直接使用成员函数
    std::result_of_t<decltype(&test)(int,int)> b = 1;   //这里注意invoke_result_t与result_of_t的使用上的区别
    //std::result_of_t<decltype(test)(int,int)> b = 1;
    std::invoke_result_t<decltype(test),int,int> a = 1;  
    auto res0 = test_decltype1(test,1,2);  //
    auto res1 = test_decltype2(test,1,3);
    auto res2 = test_decltype3(test,1,3);
    auto res3 = test_decltype4(test,1,3);

    std::cout<<"Main Res0: "<<res0<<std::endl;
    std::cout<<"Main Res1: "<<res1<<std::endl;

    return 0;

}
liuge@liuge-VirtualBox:~/Documents/mytest$ g++ -std=c++20 construct.cc -o construct
liuge@liuge-VirtualBox:~/Documents/mytest$ ./construct
Run Test3
Run Test_Decltype1
Run Test3
Run Test_Decltype2
Run Test4
Run Test_Decltype3
Run Test4
Run Test_Decltype4
Run Test4
Main Res0: 3
Main Res1: 4

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值