详解C++11中的类型推断
作者:Shawn-Summer
C++11中的类型推断
C++11中为了更好的支持泛型编程,提供了 auto
和decltype
两个关键词,目的就是提供编译阶段的自动类型推导。
1.auto
关键词的新意义
在C++98中,auto
是一个类型修饰符,用以显式声明自动变量(局部变量的),而在C++11中,这一用法已经弃用,现在auto
用于声明变量。
1.1 auto
声明变量
我们通过几个例子看一下,auto
声明变量时的规则。
int x; int *y; double foo(); int& bar(); auto *a=&x;//int * auto &b=x;//int & auto c=y;//int * auto *d=y;//int * auto e=bar();// int auto &f=bar();//int &
1.如果使得auto
声明的变量是引用类型,必须使用auto &
.
2.如果变量本身是指针类型,则auto *
和auto
是一样的,这里*
变成冗余符号
double foo(); float * bar(); const auto a=foo();//const double const auto & b=foo();//const double & volatile auto * const c=bar();//volatile float * const auto d=a;//double auto &e=a;//const double & auto f=c;//volatile float * volatile auto &g=c;//volatile float * &
3.声明为auto
的变量不能从其初始化表达式中带走顶层cv
限定符.
auto i=1,j=1.12f;//编译错误
4.auto
可以声明多个变量,不过这些变量必须类型相同
1.2 auto
无法推导的情况
#include<vector> using namespace std; void fun(auto x=1){}//错误 struct str { auto var=10;//错误 }; int main() { char x[3]; auto y=x; auto z[3]=x;//错误 vector<auto>V={1,2,3};//错误 }
auto
不能作为函数形参(这是模板函数的事情)类中,auto
不能用来声明非静态成员auto
不能声明数组auto
不能用于模板实参
2.decltype
类型推导
decltype
和auto
都是用来类型推导的,不过 decltype
的功能更加强大 ,下面就是decltype
的简单用法
2.1 decltype
的应用场景
一种情况是,decltype
和auto
一样来声明变量:
#include<typeinfo> #include<iostream> using namespace std; int main() { int i; decltype(i) j=0; cout<<typeid(j).name()<<endl;//i float a; double b; decltype(a+b) c; cout<<typeid(c).name()<<endl;//d }
另一种情况是:typedef
或using
配合使用decltype
using size_t=decltype(sizeof(0)); using ptrdiff_t=decltype((int*)0-(int*)0); using nullptr_t=decltype(nullptr);
顺便一提,在C++11中,using
已经完美替代typedef
:
#include<iostream> #include<type_traits> #include<string> #include<map> using namespace std; typedef unsigned int UINT; using uint=unsigned int; template<typename T> using Mapstring=map<T,char*>; Mapstring<int> number_string; int main() { cout<<is_same<uint,UINT>::value<<endl;//1 cout<<is_same<map<int,char*>,decltype(number_string)>::value<<endl;//1 }
typedef
能干的事情,using
都能干,但是using
能干的,比如给模板取一个别名,typedef
做不到
decltype
的另一种功能就是给匿名的结构体或者枚举推导类型来重用,
enum class {K1,K2,K3} anon_e; decltype(anon_e) as;
2.2 decltype
比auto
更加精确
首先decltype
和auto
最明显的不同就是,decltype(e)
它和sizeof()
差不多,可以接收一个参数,不过这里我讲的不同是,同一个变量,使用decltype
和auto
得到的结果不同。
说直接点,decltype
的类型推断比auto
准确
const int ic=0; decltype(ic) a;//const int auto b=ic;//int volatile int iv; decltype(iv) c;//volatile int auto d=iv;//int struct S { int i; }; const S cs={0}; decltype(cs.i) e;//int
auto
它不能带走变量的顶层cv
限定符,而decltype(e)
却可以带走e
的cv
限定符,所以说,decltype
的类型推断更加准确。还要一点细节,就是说类本身是用cv
限定符修饰的,而类成员使用decltype
时确推断不出来。
我们知道,auto
只能带走指针类型,却无法带走引用类型,而decltype
就可以同时带走引用和指针
#include<iostream> #include<type_traits> using namespace std; int main() { int i=1; decltype(i) & var1=i;// int & cout<<is_lvalue_reference<decltype(var1)>::value<<endl;//1 int &j=i; decltype(j) var2=i; decltype(j)& var3=i; cout<<is_same<decltype(j),decltype(j)&>::value<<endl;//1,`&`的冗余 int* p=&i; decltype(p) var4=&i;//int * decltype(p)* var5=&p;//int ** const int k=1; const decltype(k) var6=1;//const int `const`冗余 }
上面这段代码,信息量很大
首先,decltype(e)
可以带走e
的引用和指针类型
其次,decltype(e)
会对&
和cv
限定符产生冗余,而不会对*
产生冗余
最后,如果不确定decltype(e)
的类型,可以使用<type_traits>
头文件中的一些方法
总之,就是一句话:decltype(e)
能直接返回e
的准确类型
但是,如果decltype
更加优越,那么为什么还要auto
呢?
一种说法是
auto
用法更加简单,更重要的原因是,auto
和lambda
函数的配合使得,C++11相对于C++98,变得脱胎换骨,我个人认为C++11最重要的就是lambda
函数。
2.3 decltype
对于表达式的推断
我们知道在decltype(e)
中,e
被要求是一个表达式,即expression
,而在上面我们所讲的e
通常是一个名称,即id_expression
,如果e
是一个非名称的表达式,那么推断结果也会不同
int i; decltype(i) a;//int decltype((i)) b;//int &
在上面例子中,
i
就是一个id_expression
,而(i)
它不是id_expression
,而是一个左值表达式,所以上述推导结果会不同。
我们直接来看decltype(e)
的推导细则
- 如果
e
是id_expression
或者类成员表达式,decltype(e)
的结果就是e
本身的类型 - 否则,如果
e
是左值表达式,设它的类型是T
,那么decltype(e)
的结果就是T&
- 否则,如果
e
是将亡值,设它的类型是T
,那么decltype(e)
的结果就是T&&
- 否则,如果
e
是纯右值,设它的类型是T
,那么decltype(e)
的结果就是T
int i=4; int arr[5]={0}; int *ptr=arr; struct S { double d; }s; void foo(int); void foo(double); int && Rref();//函数返回值是将亡值 const bool Func(int); decltype(arr) var1;//int[5] decltype(ptr) var2;//int * decltype(s.d) var3;//double decltype(foo) var4;//无法通过编译,foo被重载 decltype(Rref()) var5;//int && decltype(true? i:i) var6;//int& decltype((i)) var7;//int & decltype(++i) var8;//int & decltype(arr[3]) var9;// int & decltype(*ptr) var10;//int & decltype("abc") var11;//const char(&) [4] decltype(1) var12;//int decltype(i++) var13;//int decltype(Func(1)) var14=true;//const bool
3.追踪返回类型
auto
和decltype
可以进行配合使用,来实现泛型编程中的追踪返回类型
template<class T1,class T2> decltype(t1+t2) sum(T1& t1,T2& t2) { return (t1+t2); }
上面这段代码,想法狠简单,但是它都无法通过C++11和C++98中的编译器,因为编译器是从左往右读的,读到
decltype(t1+t2)
时,t1
和t2
没有声明,所以无法通过编译,我们可以通过返回类型后置的方法实现上述功能
template<typename T1,typename T2> auto sum(T1& t1,T2& t2)->decltype(t1+t2) { return (t1+t2); }
上面就是追踪返回类型,最终
sum
函数的返回类型由decltype(t1+t2)
确定,而不是auto
确定,如果我们把->decltype(t1+t2)
去掉,那么最终返回类型就由auto
指定,我们其实很不希望这样,因为auto
并不精确,decltype
更加精确。
追踪返回类型其实就是返回类型后置,它的还有一种用法就是,提高函数指针的可读性:
#include<type_traits> #include<iostream> using namespace std; int (*(*pf())())(){ return nullptr; } auto pf1() ->auto (*)() -> int (*)() { return nullptr; } int main() { cout<<is_same<decltype(pf),decltype(pf1)>::value<<endl;//1 }
上述代码中,
pf
和pf1
都是函数指针,其返回的也是一个函数指针,该函数指针又返回一个函数指针,不过明显pf1
的定义方式可读性更高。
到此这篇关于C++11中的类型推断的文章就介绍到这了,更多相关C++11类型推断内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!