從函數(shù)模板談起
函數(shù)模板的類型推導(dǎo)機制是在C++98時代就有的务冕,auto的類型推導(dǎo)機制與其基本一致墓阀,所以先理解函數(shù)模板類型推倒
函數(shù)模板可以用如下代碼框架表示:
#template<typename T>
void f(PT param);
f(expr);
PT與T的不同之處在于PT相對于T可能有一些飾詞(adornments)主届,如const和引用&。
對于模板類型T的推導(dǎo)是PT和expr共同作用的結(jié)果轩娶。下面分幾種情況討論類型推導(dǎo)的原則:
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(px); //PT是const int*, T是const int
總結(jié)如下:
2)PT是萬能引用(universal reference, ie &&)
注:萬能引用既不是左值引用也不是右值引用泄隔,而是遇左則左拒贱,遇右則右。
原則:
- 如果expr是左值引用佛嬉,則PT和T均被推導(dǎo)為左值引用(這是所有情況中T被推導(dǎo)為引用的唯一情況)逻澳;
- 如果expr是右值引用,則與1)情況類似暖呕,即引用被忽略斜做,其余部分去匹配PT。
代碼示例如下:
//PT是T&&湾揽,左值推導(dǎo)為左值引用瓤逼,右值引用忽略
#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
結(jié)果總結(jié)如下表格
3)PT既不是指針精居,也不是引用
當(dāng)PT既不是指針,又不是引用(也就是只有T時)潜必,用傳值傳參的方式考慮問題靴姿,可以想清楚推導(dǎo)原則。
所謂傳值傳參(pass by value)意味著拷貝一份新的獨立的對象刮便,但內(nèi)容與原內(nèi)容相同空猜。
所以有如下原則和解釋::
- expr如果是引用绽慈,則忽略引用部分恨旱;
- expr的const部分,volatile部分也被忽略坝疼。(傳值傳參拷貝一份新的搜贤,原來是const,不代表新拷貝的就是const)钝凶;
- expr存在指向常量的指針(ie const int*)應(yīng)保留仪芒。 (指針拷貝了一份新的唁影,但是他指向的內(nèi)容仍然是原來的內(nèi)容,應(yīng)保證那塊內(nèi)容不可變)掂名;
代碼示例如下:
//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*锌介,指向內(nèi)容保留不可更改,指針本身const被移除猾警。
結(jié)果總結(jié)如下表格
補充1)關(guān)于數(shù)組參數(shù)的推導(dǎo)
數(shù)組參數(shù)在c++中可以退化為指針進行傳參,所以補充對數(shù)組參數(shù)的類型推導(dǎo)发皿。
原則:
- 當(dāng)PT為傳值類型(就是T)時崔慧,推導(dǎo)為退化的指針類型
- 當(dāng)PT為引用類型(T&)時,推導(dǎo)為帶有數(shù)組大小的明確的數(shù)組類型(如const char[13])穴墅;
代碼示例如下:
//數(shù)組參數(shù)
const char name[] = "C++11";
#template<typename T>
void f(T param);
f(name); //PT為 const char*,T被推導(dǎo)為const char*
#template<typename T>
void f(T& param);
f(name); //PT為const char (&)[6], T被推導(dǎo)為const char[6]
補充2)關(guān)于函數(shù)參數(shù)的推導(dǎo)
函數(shù)在c++中也可以退化為指針惶室,所以函數(shù)參數(shù)的推導(dǎo)和數(shù)組基本一致。
直接看代碼示例:
void someFunc(int, double); //類型void(int, double)
#template<Typename T>
void f1(T param);
f1(someFunc); //PT 為void(*) (int, double), T推導(dǎo)為 void(*) (int, double)玄货;
#template<Typename T>
void f2(T& param);
f2(SomeFunc); //PT為void(&)(int, double)拇涤, T推導(dǎo)為 void(int, double);
函數(shù)模板到auto
auto的類型推導(dǎo)機制與函數(shù)模板類型推導(dǎo)機制幾乎完全一樣,除了一個小的注意點誉结。
回顧函數(shù)模板類型推導(dǎo)的代碼框架:
#template<typename T>
void f(PT param);
f(expr);
推導(dǎo)出T鹅士,需要PT與expr的共同作用。
在帶有auto的表達式中惩坑,auto代表的就是T掉盅,也就是最后要推導(dǎo)出的結(jié)果;等號右邊代表expr以舒;而auto與其飾詞一起趾痘,表示的是PT
注意此時最后更關(guān)心的是PT的類型,也就是式中待推導(dǎo)變量的類型蔓钟。如 :
int x = 11永票;
const auto& rx = x; //auto代表T, const auto&代表PT, x代表expr
所以第一部分中討論的函數(shù)模板類型推導(dǎo)原則完全適用于auto的類型推導(dǎo)。
給出代碼示例如下:
auto x = 11; //類型3滥沫, auto推導(dǎo)為int
const auto cx = x; // 類型3, auto推導(dǎo)為int, cx類型為int
const auto& rx = x; //類型1侣集, auto推導(dǎo)為int, rx類型為int&
auto&& uref1 = x; //左值,auto推導(dǎo)為int&, uref1類型為int&
auto&& uref2 = cx; //左值兰绣,auto推導(dǎo)為const int&, uref2類型為const int&
auto&& uref3 = 11; //右值世分,auto推導(dǎo)為int, uref3類型 int&&
const char name[] = "C++11" ;
auto arr1 = name; // auto推導(dǎo)為const char*, arr1類型const char*
auto& arr2 = name; //auto推導(dǎo)為const char[6]缀辩, arr2類型為const char(&)[6]
void someFunc(int, double);
auto func1 = someFunc(); //auto推導(dǎo)為void(*)(int, double)臭埋, func1類型為void(*)(int, double)
auto& func2 = someFunc();//auto推導(dǎo)為void(int, double)踪央,func2類型為void(&)(int, double)
注:
- 上述原則都是與函數(shù)模板推導(dǎo)一直的原則。一點不同在于函數(shù)模板沒有對于{}的推導(dǎo)瓢阴,這是auto獨有的畅蹂。
使用{}初始化,會被推導(dǎo)為std:initializer_list的相關(guān)類型荣恐。如:
auto x3 = {27}; //推導(dǎo)為std::initializer_list<int>;
- 在函數(shù)返回類型和lambda表達式參數(shù)中使用auto魁莉,使用的是模板參數(shù)類型推導(dǎo),而不是auto的類型推導(dǎo)募胃;
也就是在此情況下旗唁,推導(dǎo){}會報錯。如:
auto createList() { //error痹束,模板參數(shù)類型推導(dǎo)不能處理{}
return {1,2,3};
}
本文總結(jié)了函數(shù)模板參數(shù)推導(dǎo)检疫,進而推廣到auto的類型推導(dǎo)。