上一篇帖子中讲述了模板类型推断,咱们知道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 }; // }
最后总结一下: