12C++11新手易學,老兵易用_1
1. auto 關鍵字的限制
auto 雖然功能很強大,但是也不是萬能的,受制於語法的二義性,或者是實現的困難性, auto 往往也會有使用上的限制。
#include <vector> //1.auto 函式引數,無法通過編譯 void fun(auto x = 1) {} struct str { //2.auto 非靜態成員變數,無法通過編譯 auto var = 10; } int main() { char x[3]; auto y = x; //3.auto 陣列,無法通過編譯 auto z[3] = x; //4.auto 模板引數(例項化時),無法通過編譯 vector<auto> v = { 1 }; reutrn 0; }
2. decltype
2.1 引入
c++98 對型別推導的支援採用執行時型別識別的機制。
void testDecltype() { cout << "enter testDecltype()...................................................................." << endl; While a; Black b; cout << "typeid(a).name():" << typeid(a).name() << endl; //typeid(a).name() :class While cout << "typeid(a).hash_code():" << typeid(a).hash_code() << endl; //typeid(a).hash_code() :2404998906(每次執行返回的值都是一樣的) cout << "typeid(b).name():" << typeid(b).name() << endl; //typeid(b).name() : class Black cout << "typeid(b).hash_code():" << typeid(b).hash_code() << endl; //typeid(b).hash_code() :2071569968 While c; cout << "typeid(c).name():" << typeid(c).name() << endl; //typeid(c).name() : class While cout << "typeid(c).hash_code():" << typeid(c).hash_code() << endl; //typeid(c).hash_code() :2404998906 MyStruct stru; MyStruct1 stru1; cout << "typeid(c).name():" << typeid(stru.a).name() << endl; //typeid(c).name() : int cout << "typeid(stru.c).name():" << typeid(stru.c).name() << endl; //typeid(stru.c).name() : char cout << "typeid(c).name():" << typeid(stru1.m).name() << endl; //typeid(c).name() : struct MyStruct }
decltype 的型別推導並不是像 auto 一樣是從變數宣告的初始化標識獲得變數的型別, decltype 總是以一個普通的表示式為引數,返回該表示式的型別。
而與 auto 相同的是,作為一個型別指示符, decltype 可以將獲得的型別來定義另外一個變數。
另外,與 auto 相同, decltype 型別推導也是在編譯時進行的。
2.2 應用
在 c++11 中,使用 decltype 推導型別是非常常見的事情。比較典型的就是 decltype 與 tydef/using 的合用。在 c++11 標頭檔案中,我們常能看到以下這樣的程式碼:
using size_t = decltype(sizeof(0)); using ptrdiff_t = decltype((int*)0 - (int*0)); using nullptr_t = decltype(nullptr);
在 c++ 中,我們有時會遇到匿名的型別,而擁有了 decltype 這個利器之後,重用匿名型別也並非難事。
struct
{
int a;
} stru;
void test()
{
decltype(stru) stru1;
stru1.a = 11;
}
事實上,在一些 c 程式碼中,匿名的結構體和聯合體並不少見。不過匿名一般都有匿名理由,一般程式設計師都不希望匿名後的型別被重用。這裡 decltype 只是提供了一種語法上的可能。
2.3 推導四規則
大多時候, decltype 的使用看起來非常平易近人,但是有時我們也會落入一些令人疑惑的陷阱。
int i;
decltype(i) a; //a:int
decltype((i)) b; //b: int& 無法通過編譯
decltype((i)) g = i; // 可以通過編譯
具體的,當程式設計師用 decltype(e) 來獲取型別時,編譯器將依序判斷以下四規則:
-
(1)如果 e 是一個沒有帶括號的標記符表示式或者類成員訪問表示式,那麼 decltype(e) 就是 e 所命名的實體的型別。此外,如果 e 是一個被過載的函式,則會導致編譯時錯誤。
-
(2)否則,假設 e 的型別是 T , 如果 e 是一個將亡值,那麼 decltype(e) 為 T&&。
-
(3)否則,假設 e 的型別是 T,如果 e 是一個左值, 則 decltype(e) 為 T&。
-
(4)否則,假設 e 的型別是 T,則 decltype 為 T。
在上面的程式碼中,decltype(i) a; 使用了推導規則1——因為 i 是一個標記符表示式(基本上,所有除去關鍵字、字面量量 等編譯器需要使用的標記之外的程式設計師自定義的標記都可以是標記符。而單個標記符對應的表示式就是標記符表示式),所以型別被推導為 int。而 decltype((i)) b; 中,由於 (i) 不是一個標記符表示式,但卻是一個左值表示式(可以由具名的地址),因此,按照推導規則3,器型別應該是一個 int&。
int arr[5] = { 0 };
int* ptr = arr;
struct S
{
double d;
}s;
void overloaded(int);
void overloaded(char);
int && rValRef();
const bool foo(int);
//規則1:單個標記符表示式以及訪問類成員,推導為本型別
decltype(arr) var1; //int[5], 標記符表示式
decltype(ptr) var2; //int*, 標記符表示式
decltype(s.d) var4; //double, 成員訪問表示式
//decltype(overloaded) var5; //無法通過編譯,是個過載函式
//規則2: 將亡值,推導為型別的右值引用
decltype(rValRef()) var6 = 1; // int&&
//規則3: 左值,推導為型別的引用
decltype(true ? i : i) var7 = i; //int&, 三元運算子,這裡返回一個 i 的左值
decltype((i)) var8 = i; //int&, 帶圓括號的左值
decltype(++i) var9 = i; //int&, ++i 返回 i 的左值
decltype(arr[3]) var10 = i; //int&, [] 操作返回左值
decltype(*ptr) var11 = i; //int&, *操作返回左值
decltype("lval") var12 = "lval"; //const char(&)[5],字串字母常量為左值
//規則4:以上都不是,推導為本型別
decltype(1) var13; //int, 除字串外字面常量值為右值
decltype(i++) var14; //int, i++ 返回右值
decltype((foo(1))) var15; //const bool, 圓括號可以忽略
使用 decltype 時,一個簡單的能夠讓編譯器提示的方法是:先宣告這個變數,再在其他語句裡對其進行初始化。這樣一來,由於左值引用總是需要初始化的,編譯器會報錯提示。
2.4 cv 限制符的繼承與冗餘的符號
與 auto 型別推導時不能 “帶走” cv 限制符不同, decltype 是能夠 “帶走” 表示式的 cv 限制符的。不過,如果物件的定義中有 const 或 volatile 限制符,使用 decltype 進行推導時,器成員不會繼承 const 或則 volatile 限制符。
const int ic = 0;
volatile int iv;
struct SS { int i; };
const SS a = { 0 };
volatile SS b;
volatile SS* pp = &b;
cout << is_const<decltype(ic)>::value << endl; //1
cout << is_volatile<decltype(iv)>::value << endl; //1
cout << is_const<decltype(a)>::value << endl; //1
cout << is_volatile<decltype(b)>::value << endl; //1
cout << is_const<decltype(a.i)>::value << endl; //0, 成員不是 const
cout << is_volatile<decltype(pp->i)>::value << endl;//0 , 成員不是 volatile