一.decltype關鍵字介紹
decltype關鍵字與auto關鍵字相似,但又有不同之處;auto關鍵字是在編譯時通過已經初始化的變量來確定auto所代表的類型。換句話說,auto修飾的表達式必須是已經初始化的變量;那么如果我們只是想得到此變量的類型,那又該如何做呢?這個時候就輪到decltype出場了,decltype關鍵字也是用來在編譯時推導出一個表達式的類型,但此表達式初始化與否,在編譯器都沒有多大的影響。
下面是使用關鍵字decltype關鍵字的示例:
#include <iostream> using namespace std; int main() { int x = 0; decltype(x) y = 1; //y -> int cout << y << endl; decltype(x + y) z = 2; //z -> int cout << z << endl; const int& i = x; decltype(i) j = y; //j -> const int & cout << j << endl; const decltype(z) *p = &z; //*p -> const int decltype(z) *pi = &z; //*pi -> int, pi->int* decltype(pi) *pp = π //*pp -> int*, pp->int** cout << pp << endl; //打印結果:0x61fe80 cout << *pp << endl; //打印結果:0x61fe84 cout << **pp << endl; //打印結果:2 return 0; }
A.y和z的結果表明decltype可以根據表達式直接推導出變量的類型,這個功能和auto很像,但又有所不同,auto只能根據變量的初始化表達式推導出變量應該具有的類型,如果想要通過某個表達式得到類型,但又不希望新變量和這個表達式具有同樣的值,那么這個時候auto就不太適用了。
B.j的結果表明decltype通過表達式得到的類型,可以保留表達式的引用及const限定符,decltype能夠精確地推導出表達式定義本身的類型,不會像auto那樣在某些情況下舍棄掉引用和cv限定符。
C.p、pi的結果表明decltype可以像auto一樣,加上引用和指針,以及cv限定符。
D.pp的推導說明,當表達式是一個指針的時候,decltype仍然能夠推導出表達式的實際類型(指針類型),之后結合pp定義時的指針標記,得到的pp是一個二維指針類型
二.decltype的推導規則
decltyp(exp)的推導規則如下所示:
(1).標識符表達式和類訪問表達式。
(2).函數調用(非標識符表達式,也非類訪問表達式)。
(3).帶括號的表達式和加法運算表達式(其他情況)。
先來說第一種情況:
#include <iostream> using namespace std; class Test { public: Test() {} public: static const int nNumber = 0; int x; }; int main() { //類訪問表達式 decltype(Test::nNumber) c = 10; Test test; decltype(test.x) d = 20; cout << c << "," << d <<endl; //標識符表達式 int y = 20; decltype(y) z = 30; return 0; }
第二種情況:函數調用
#include <iostream> using namespace std; class Test { public: Test() {} public: int m_nNum = -10; }; int Test_Int(); //純右值 int& Test_Int_One(); //左值 const int Test_Cint(); //純右值 const int& Test_Cint_One(); //左值 const Test Test_Class(); //純右值 int main() { int x = 10; decltype(Test_Int()) a1 = x; //a1->int cout << a1 << endl; decltype(Test_Cint_One()) a2 = x; //a2->int& int y = a2; cout << y << endl; cout << a2 << endl; decltype(Test_Cint()) b1 = x; //b1->const int b1 = 20; cout << b1 << endl; decltype(Test_Cint_One()) b2 = x; //b2->const& int //b2 = 30; //error:b2只是一個只讀引用,不可再為其賦值 cout << b2 << endl; decltype(Test_Class()) c1 = Test(); cout << c1.m_nNum << endl; return 0; }
能夠看出按照規則2推導decltype的結果和函數的返回值類型保持一致,但需要注意的是b1是int而不是const int,這是因為函數返回的int是一個純右值,對於純右值而言,只有類類型可以攜帶cv限定符,除此之外的一般都會忽略掉cv限定符;因此decltype推導出來的b1是一個int類型,而c1推導出的類型是const Test。
第三種:帶括號的表達式和加法運算表達式
#include <iostream> using namespace std; class Test { public: Test() {} public: static const int nNum = -10; int x = 0; }; int main() { const Test test = Test(); decltype(test.x) a = 0; //a->int decltype((test.x)) b = a; //b->const int& b = 10; //error:b為只讀引用 cout << b << endl; int m =0,n=0; decltype(m + n) c = 10; //c->int decltype(m += n) d = c; //d ->int & return 0; }
a和b的結果:僅僅多加了一對括號,它們得到的類型卻不是相同的。
a的結果很明顯,根據推導規則1,a的類型就是test.x的定義類型。
b的結果並不適用與推導規則1和推導規則2,根據test.x是一個左值,可知括號表達式也是一個左值,因此可以根據推導規則3,知道decltype的結果將是一個左值引用;因為test的定義時const Test,所以說foo.x是一個const int類型左值,因此decltype的推導結果是const int&.
同樣的,m+n返回一個右值,decltype的結果為int。最后,m+=n返回一個左值,根據推導規則3,decltype的結果為int&.