上一篇帖子中講述了模板類型推斷,咱們知道auto的實現原理是基於模板類型推斷的,回顧一下模板類型推斷:html
template <typename T> void f(ParamType param);
使用下面的函數調用:數組
f(expr);
咱們看到模板類型推斷過程涉及到了模板template、函數f以及參數(包括模板參數和函數參數),調用f的時候,編譯器會推斷T和ParamType的類型。auto的實現和這三個部分是有着對應關係的。當使用auto聲明一個變量,auto關鍵字扮演的是模板類型推斷中T的角色,而類型說明符扮演的是ParamType的角色。看下面的例子:函數
auto x = 27; //類型說明符就是auto本身 const auto cx =x; //類型說明符爲const auto const auto& rx =x;//類型說明符爲const auto&
編譯器使用auto對上面的類型進行推斷就如同使用了下面的模板類型推斷:指針
template<typename T> void func_for_x(T param); //ParamType即非引用也非指針 func_for_x(27); // 推斷x的類型,T爲int ,ParamType 爲 int template<typename T> void func_for_cx(const T param); //ParamType即非引用也非指針 func_for_cx(x); //用於推斷cx的類型,T爲int,ParamType爲 const int template<typename T> void func_for_rx(const T& param);//ParamType爲引用 func_for_rx(x); // 用於推斷rx的類型,T爲int,ParamType爲const int&
繼續回顧上一篇帖子的內容,基於ParamType的三種形式,模板類型推斷也對應着三種不一樣狀況。而auto的類型說明符扮演的是ParamType,所以使用auto進行變量聲明,也會有三種狀況:code
上面舉的例子是第一種和第三種狀況:orm
auto x = 27; //case 3 x類型被推斷爲int const auto cx = x; //case 3 cx被推斷爲 const int const auto &rx = x; //case 1 rx被推斷爲const int &
舉一個狀況2的例子:htm
auto&& uref1 = x; //x爲左值,uref1被推斷爲左值引用 auto&& uref2 = cx; // cx const int 左值,uref2被推斷爲const int & auto&& uref3 = 27; // 27 爲 int 右值,uref3被推斷爲 int &&
上篇帖子介紹了對於模板中的非引用ParamType,傳入函數或者數組實參的時候會退化爲指針的狀況(而使用引用ParamType的時候,數組實參會被推斷爲指向數組的引用),auto類型推斷也會如此:blog
const char name[] = "R. N. Briggs"; auto arr1 = name; // arr1 的類型爲const char* auto& arr2 = name; // arr2 的類型爲const char (&)[13] void someFunc(int, double); auto func1 = someFunc; // func1的 類型爲 void (*)(int, double) auto& func2 = someFunc; // func2的類型爲 void (&)(int, double)
上面介紹的都是auto和模板類型推斷使用原理相同的部分,下面說的不同的。get
C++98中初始化一個Int有兩種方式:編譯器
int x1=27; int x1(27);
在C++11中,支持統一初始化(uniform initialization):
int x3 = {27}; int x3{27};
四種語法形式的結果只有一個,初始化一個Int值爲27。這裏咱們將都使用auto進行初始化:
auto x1 = 27; auto x2(27); auto x3 = {27}; auto x4{27};
上面的四句話都能編譯經過,但並無和原來的四種形式意義徹底一致。前面兩個是同樣的,後面兩句話聲明的變量類型是std::initializer_list
auto x1 = 27; //x1爲int,值爲27 auto x2(27);//同上 auto x3 = {27};//x3爲 std::initializer_list<int>,值爲{27} auto x4{27}; //同上
這裏就用到了一個對於auto的特殊類型推斷規則:當用大括號括起來的值對auto變量進行初始化的時候(叫作統一初始化式),變量類型會被推斷爲 std::initializer_list。若是不可以推斷成此類型(好比,大括號中的值不是同一類型),編譯會出錯:
auto x5 = { 1, 2, 3.0 }; // error! 類型不一致,不能將推斷爲std::initializer_list<T>
這裏會發生兩種類型推斷,一種是將統一初始化式推斷爲std::initializer_list
對統一初始化式的處理的不一致是auto和模板類型推斷的惟一區別。使用統一初始化式對auto變量初始化會將其推斷爲std::initializer_list
auto x = { 11, 23, 9 }; // x的類型爲 std::initializer_list<int> template<typename T> // 和auto x等同的模板類型推斷 void f(T param); f({ 11, 23, 9 }); // 錯誤!這裏不能推斷T的類型。
若是要達到auto的效果,得按照下面的方式來作:
template<typename T> void f(std::initializer_list<T> initList); f({ 11, 23, 9 }); // T被推斷爲int, initList 的類型爲 std::initializer_list<int>
在C++11中使用auto時,這裏比較容易出錯,你原本想聲明別的變量,最終卻將其聲明成了一個 std::initializer_list
在C++14中,容許將auto做爲函數返回值,也能夠用其修飾lambda表達式中的參數。可是這些auto使用的都是模板類型推斷,而不是auto類型推斷,所以一個函數返回值爲auto 類型時,返回統一初始化式的值會出錯:
auto createInitList() { return { 1, 2, 3 }; // 錯誤!不能推斷{1,2,3} }
下面的方式是對的:
std::initializer_list<int> createInitList() { return { 1, 2, 3 }; // }
最後總結一下: