C++ auto 類型推導規則與模板類型推導類似,兩者只有一點不一樣。
前面說過,模板的聲明和使用方式如下:
template<typename T>
void f(ParamType param);
f(expr);
auto的使用方式如下:
auto x = 27;
const auto cx = x;
const auto& rx = x;
在auto進行類型推導的時候,auto相當于模板里的T,變量類型修飾符相當于ParamType,等號右邊的部分相當于expr。例如:
auto x = 27;
// 進行x的類型推導,就如同下面的模板類型推導
template<typename T>
void func_for_x(T param);
func_for_x(27);
// 27是int型,根據上篇文章的推導規則,param是int型,因此x也是int型。
const auto& rx = x;
// 進行rx的類型推導,就如同下面的模板類型推導
template<typename T>
void func_for_rx(const T& param);
func_for_rx(x);
// x是int型,模板推導出的param類型是const int&,因此rx也是const int&。
auto類型推導規則根據類型修飾符也分三種情形,與模板類型推導完全一致,對數組參數和函數參數的處理也完全一致,不再贅述。
開頭說過,有一種情形auto與模板類型推導不一致。C++98初始化變量有兩種方式:
int x1 = 27;
int x2(27);
C++11增加了統一初始化語法:
int x3 = { 27 };
int x4{ 27 };
這四種初始化方式效果都一樣,把變量初始化為27。
但是如果把變量類型改成auto:
auto x1 = 27;
auto x2(27);
auto x3 = { 27 };
auto x4{ 27 };
此時,前兩個變量的類型還是int。后面兩個變量的類型變成了std::initializer_list<int>
。
這是auto推導規則的一個特殊的地方,如果auto的變量初始化時使用了大括號,推導類型就是std::initializer_list
。然而,模板推導時如果使用了大括號,則推導失敗。
auto x = { 11, 23, 9 }; // x類型是std::initializer_list<int>
template<typename T>
void f(T param);
f({ 11, 23, 9 }); // 編譯失敗,無法推導出{ 11, 23, 9 }類型
如果把模板參數改為如下形式,則可推導成功:
template<typename T>
void f(std::initializer_list<T> initList);
f({ 11, 23, 9 }); // 編譯通過,initList類型是std::initializer_list<int>,T是int型
因此auto和模板類型推導的區別在于,auto會把大括號初始化推導為std::initializer_list,模板不會。
最后,還有一點需要注意的地方,C++14允許指定函數返回值為auto,C++14的lambda表達式也允許參數類型為auto。這兩種情況下的auto類型推導采用模板類型推導的規則。
因此下面兩段代碼都編譯不通過:
auto createInitList() {
return { 1, 2, 3 }; // 編譯失敗,無法推導{ 1, 2, 3 }類型
}
std::vector<int> v;
auto resetV = [&v](const auto& newValue) { v = newValue; };
resetV({ 1, 2, 3 }); // 編譯失敗,無法推導{ 1, 2, 3 }類型
總結:
- auto類型推導與模板類型推導基本一致,除了auto把大括號初始化推導為std::initializer_list,模板不會。
- 函數返回值和lambda表達式參數中的auto采用模板類型推導的規則。