我們之前使用的typeid運算符來查詢一個變量的類型,這種類型查詢在運行時進行。RTTI機制為每一個類型產生一個type_info類型的數據,而typeid查詢返回的變量相應type_info數據,通過name成員函數返回類型的名稱。同時在C++11中typeid還提供了hash_code這個成員函數,用於返回類型的唯一哈希值。RTTI會導致運行時效率降低,且在泛型編程中,我們更需要的是編譯時就要確定類型,RTTI並無法滿足這樣的要求。編譯時類型推導的出現正是為了泛型編程,在非泛型編程中,我們的類型都是確定的,根本不需要再進行推導。
而編譯時類型推導,除了我們說過的auto關鍵字,還有本文的decltype。
decltype與auto關鍵字一樣,用於進行編譯時類型推導,不過它與auto還是有一些區別的。decltype的類型推導並不是像auto一樣是從變量聲明的初始化表達式獲得變量的類型,而是總是以一個普通表達式作為參數,返回該表達式的類型,而且decltype並不會對表達式進行求值。
int i = 4;
decltype(i) a; //推導結果為int。a的類型為int。
using size_t = decltype(sizeof(0));//sizeof(a)的返回值為size_t類型
using ptrdiff_t = decltype((int*)0 - (int*)0);
using nullptr_t = decltype(nullptr);
vector<int >vec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin; i != vec.end(); i++)
{
//...
}
這樣和auto一樣,也提高了代碼的可讀性。
在C++中,我們有時候會遇上一些匿名類型,如:
struct
{
int d ;
doubel b;
}anon_s;
而借助decltype,我們可以重新使用這個匿名的結構體:
decltype(anon_s) as ;//定義了一個上面匿名的結構體
這也是decltype最大的用途了。
template <typename _Tx, typename _Ty>
auto multiply(_Tx x, _Ty y)->decltype(_Tx*_Ty)
{
return x*y;
}
標記符指的是除去關鍵字、字面量等編譯器需要使用的標記之外的程序員自己定義的標記,而單個標記符對應的表達式即為標記符表達式。例如:
int arr[4]
則arr為一個標記符表達式,而arr[3]+0不是。
我們來看下面這段代碼:
int i=10;
decltype(i) a; //a推導為int
decltype((i))b=i;//b推導為int&,必須為其初始化,否則編譯錯誤
僅僅為i加上了(),就導致類型推導結果的差異。這是因為,i是一個標記符表達式,根據推導規則1,類型被推導為int。而(i)為一個左值表達式,所以類型被推導為int&。
通過下面這段代碼可以對推導四個規則作進一步了解
int i = 4;
int arr[5] = { 0 };
int *ptr = arr;
struct S{ double d; }s ;
void Overloaded(int);
void Overloaded(char);//重載的函數
int && RvalRef();
const bool Func(int);
//規則一:推導為其類型
decltype (arr) var1; //int 標記符表達式
decltype (ptr) var2;//int * 標記符表達式
decltype(s.d) var3;//doubel 成員訪問表達式
//decltype(Overloaded) var4;//重載函數。編譯錯誤。
//規則二:將亡值。推導為類型的右值引用。
decltype (RvalRef()) var5 = 1;
//規則三:左值,推導為類型的引用。
decltype ((i))var6 = i; //int&
decltype (true ? i : i) var7 = i; //int& 條件表達式返回左值。
decltype (++i) var8 = i; //int& ++i返回i的左值。
decltype(arr[5]) var9 = i;//int&. []操作返回左值
decltype(*ptr)var10 = i;//int& *操作返回左值
decltype("hello")var11 = "hello"; //const char(&)[9] 字符串字面常量為左值,且為const左值。
//規則四:以上都不是,則推導為本類型
decltype(1) var12;//const int
decltype(Func(1)) var13=true;//const bool
decltype(i++) var14 = i;//int i++返回右值
這裡需要提示的是,字符串字面值常量是個左值,且是const左值,而非字符串字面值常量則是個右值。
這麼多規則,對於我們寫代碼的來說難免太難記了,特別是規則三。我們可以利用C++11標准庫中添加的模板類is_lvalue_reference來判斷表達式是否為左值:
cout << is_lvalue_reference<decltype(++i)>::value << endl;
結果1表示為左值,結果為0為非右值。
同樣的,也有is_rvalue_reference這樣的模板類來判斷decltype推斷結果是否為右值。
參考資料:《深入理解C++11》 pdf下載 http://www.linuxidc.com/Linux/2015-12/126521.htm