1.從函數模板談起
函數模板的類型推導機制是在c++98時代就有的,auto的類型推導機制與其基本一致,所以先理解函數模板類型推導。
函數模板可以用如下代碼框架表示:
#template<typename T> void f(PT param); f(expr);
PT與T的不同之處在於PT相對於T可能有一些飾詞(adornments),如const 和引用&。
對於模板類型T的推導是PT和expr共同作用的結果。下面分幾種情況討論類型推導的原則:
1)PT是一般的引用或指針
原則:
- expr是一個引用(指針)類型,忽略其引用(指針)部分;
- expr其他部分去匹配PT得到T的類型。 (匹配原則就是如果PT有const則expr的const忽略,PT無const,expr有,則添加)
代碼示例如下:
//PT是指針或引用類型,expr的引用或指針被忽略 //PT是T& #template<typename T> void f(T& param); int x = 11; const int cx = x; const int& rx = x; f(x); //PT是int&, T是int f(cx); //PT是const int&, T是const int f(rx); //PT是const int&, T是const int //PT是const T&,expr中的const被忽略 #template<typename T> void f(const T& param); int x = 11; const int cx = x; const int& rx = x; f(x); //PT是int&, T是int f(cx); //PT是const int&, T是int f(rx); //PT是const int&, T是int //PT是T*, #template<typename T> void f(T* param); int x = 11; const int* px = &x; f(&x); //PT是int*, T是int f(rx); //PT是const int*, T是const int
結果總結如下表格:
2) PT是萬能引用(universal reference, ie &&)
注:萬能引用既不是左值引用也不是右值引用,而是遇左則左,遇右則右。
原則:
- 如果expr是左值引用,則PT和T均被推導為左值引用(這是所有情況中T被推導為引用的唯一情況);
- 如果expr是右值引用,則與1)情況類似,即引用被忽略,其余部分去匹配PT。
代碼示例如下:
//PT是T&&,左值推導為左值引用,右值引用忽略 #template<typename T> void f(T&& param); int x = 11; const int cx = x; const int& rx = x; f(x); //x是左值, PT是int&, T是int& f(cx); //cx是左值, PT是const int&, T是const int& f(rx); //rx是左值, PT是const int&, T是const int& f(11); //11是右值, PT是int&&, T是int
結果總結如下表格:
3)PT既不是指針,也不是引用
當PT既不是指針,又不是引用(也就是只有T)時,用傳值傳參的方式考慮問題,可以想清楚推導原則。
所謂傳值傳參(pass by value)意味着拷貝一份新的獨立的對象,但內容與原內容相同。
所以有如下原則和解釋:
- expr如果是引用,則忽略引用部分;
- expr的const部分,volatile部分也被忽略。(傳值傳參拷貝一份新的,原來是const,不代表新拷貝的就是const);
- expr存在指向常量的指針(ie const int*)應保留。 (指針拷貝了一份新的,但是他指向的內容仍然是原來的內容,應保證那塊內容不可變);
代碼示例如下:
//PT是T #template<typename T> void f(T param); int x = 11; const int cx = x; const int& rx = x; const char* ptr1 = "C++11"; const char* ptr2 const = "c++14"; f(x); //PT是int&, T是int f(cx); //PT是int, T是int f(rx); //PT是int, T是int f(ptr1);//PT是const char*, T是const char* f(ptr2);//PT是const char*,T是const char*,指向內容保留不可更改,指針本身const被移除。
結果總結如下表格:
補充1)關於數組參數的推導
數組參數在c++中可以退化為指針進行傳參,所以補充對數組參數的類型推導。
原則:
- 當PT為傳值類型(就是T)時,推導為退化的指針類型;
- 當PT為引用類型(T&),推導為帶有數組大小的明確的數組類型(如const char[13]);
代碼示例如下:
//數組參數 const char name[] = "C++11"; #template<typename T> void f(T param); f(name); //PT為 const char*,T被推導為const char* #template<typename T> void f(T& param); f(name); //PT為const char (&)[6], T被推導為const char[6]
補充2) 關於函數參數的推導
函數在c++中也可以退化為指針,所以函數參數的推導和數組基本一致。
直接看代碼示例:
void someFunc(int, double); //類型void(int, double) #template<Typename T> void f1(T param); f1(someFunc); //PT 為void(*) (int, double), T推導為 void(*) (int, double); #template<Typename T> void f2(T& param); f2(SomeFunc); //PT為void(&)(int, double), T推導為 void(int, double);
總結如下表格:
至此,關於函數模板的推導過程總結完畢。
2. 函數模板到auto
auto的類型推導機制與函數模板類型推導機制幾乎完全一樣,除了一個小的注意點。
回顧函數模板類型推導的代碼框架:
#template<typename T> void f(PT param); f(expr);
推導出T,需要PT與expr的共同作用。
在帶有auto的表達式中,auto代表的就是T,也就是最后要推導出的結果;等號右邊代表expr;而auto與其飾詞一起,表示的是PT
注意此時最后更關心的是PT的類型,也就是式中待推導變量的類型。如 :
int x = 11; const auto& rx = x; //auto代表T, const auto&代表PT, x代表expr
所以第一部分中討論的函數模板類型推導原則完全適用於auto的類型推導。
給出代碼示例如下:
auto x = 11; //類型3, auto推導為int const auto cx = x; // 類型3, auto推導為int, cx類型為int const auto& rx = x; //類型1, auto推導為int, rx類型為int& auto&& uref1 = x; //左值,auto推導為int&, uref1類型為int& auto&& uref2 = cx; //左值,auto推導為const int&, uref2類型為const int& auto&& uref3 = 11; //右值,auto推導為int, uref3類型 int&& const char name[] = "C++11" ; auto arr1 = name; // auto推導為const char*, arr1類型const char* auto& arr2 = name; //auto推導為const char[6], arr2類型為const char(&)[6] void someFunc(int, double); auto func1 = someFunc(); //auto推導為void(*)(int, double), func1類型為void(*)(int, double) auto& func2 = someFunc();//auto推導為void(int, double),func2類型為void(&)(int, double)
注:
1). 上述原則都是與函數模板推導一致的原則。一點不同在於函數模板沒有對於{}的推導,這是auto獨有的。
使用{}初始化,會被推導為std:initializer_list的相關類型。如:
auto x3 = {27}; //推導為std::initializer_list<int>;
2). 在函數返回類型和lambda表達式參數中使用auto,使用的是模板參數類型推導,而不是auto的類型推導;
也就是在此情況下,推導{}會報錯。如:
auto createList() { //error,模板參數類型推導不能處理{} return {1,2,3}; }
至此,關於auto類型推導總結完畢。
參考文獻:
1. Effective Modern C++, Scott Meyers, http://shop.oreilly.com/product/0636920033707.do
2. C++ 11/14最佳實踐, 高博, http://boolan.com/camp/4