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