之前讨论了C11中有关模板函数类型推导,今天接着讨论学习下auto关键字的类型推导。
如下代码片段,如果用auto声明一个变量,那该如何推导出变量的类型呢;
auto x = 27; // x 的类型是什么呢
const auto cx = x; // cx 的类型又是什么呢
auto类型推导机理
在介绍模板函数类型推导时,是通过形如下面这样的代码调用模板函数,进而让编译器推导出 T 与 ParamType 的类型;
//声明模板函数
template <typename T>
void f(ParamType param);
//调用模板函数
f(expr);
如果用auto声明一个变量来推导出变量类型时,将这种情况类比到模板类型推导,则 auto 就扮演了 T 的角色,变量的类型饰词就扮演了 ParamType 的角色,编译器对 auto 的类型推导过程就相当于是 编译器声明了一个模板函数同时利用变量的初始化表达式实现了针对该模板函数的一次调用;然后推导出 T 与 ParamType 的类型;
auto x = 27; // 此时 auto 相当于 T, auto 也相当于 ParamType
//类比到模板函数,声明一个模板函数
template <typename T>
void for_x(T param);
//针对该模板函数的一次调用
for_x(27);
const auto cx = x; // 此时 auto 相当于 T, const auto 相当于 ParamType
//类比到模板函数,声明一个模板函数
template <typename T>
void for_cx(const T param);
//针对该模板函数的一次调用
for_cx(x);
const auto &rx = x; // 此时 auto 相当于 T, const auto & 相当于 ParamType
//类比到模板函数,声明一个模板函数
template <typename T>
void for_rx(const T& param);
//针对该模板函数的一次调用
for_rx(x);
以上代码片段中声明模板函数与调用语句只是为了辅助理解auto关键字的类型推导,并不是编译器真正生成了这样的模板函数以及调用语句,切记。
auto类型推导规则
既然可以将auto关键字的类型推导类比到模板函数上,那么 auto 关键字的类型推导规则也可以类比到模板函数的推导规则上,事实也确实如此。二者之间的推导规则大差不差,基本完全一致,auto的推导中除了有一点比较特殊的例子外(下面会介绍),它们的推导规则之间也是可以建立起一一映射的。根据ParamType的形式,同样需要分三种情况来讨论的:
a. 类型饰词为指针或引用,但非万能引用;
b. 类型饰词为万能引用;
c. 类型饰词既非指针也非引用;
看如下代码片段:
auto x = 27; // x 的类型既非指针也非引用,情况c, x 类型推导为 int
const auto cx = x; // cx 的类型既非指针也非引用,情况c, cx 类型推导为 const int
const auto &rx = x; // rx 的类型为引用,情况a, rx 类型推导为 const int &
auto&& lx = x; // lx 的类型为万能引用, 情况b, x 为左值,lx 的类型推导为 int&
auto&& lx2 = cx; // lx2 的类型为万能引用, 情况b, cx 为左值,lx2 的类型推导为 const int&
auto&& lx3 = 27; // lx3 的类型为万能引用, 情况b, 27 为右值,lx3 的类型推导为 int&&
模板函数中对于数组与函数的推导规则同样适用于auto的推导中:
const char name[] = "csdn blogs"; //name 的类型为 const char [11]
auto arr = name; //arr 的类型为 const char *
auto &arr_2 = name; // arr_2 的类型为 const char (&)[11]
void func_example(int, double); // 函数类型为 void(int, double)
auto fun = func_example; // fun的类型为 void(*)(int, double) 函数指针
auto &fun2 = func_example; //fun2的类型为 void(&)(int, double) 函数引用
auto推导中的特例
我们都知道C++11标准发布以来,初始化一个变量有了不同的语法,也可以用花括号{}来初始化一个变量的值,这样称为C++11统一了初始化的语法;如下代码语句都是合法的:
int x = 27;
int y(27);
int z = {27};
int w{27};
但如果将上面代码中的具体类型 int 换成 auto 的话,代码语句也都是合法的,但是推导出来的变量类型却有所改变,这也是 auto 推导规则中最特殊的一条了;
auto x = 27; // x 的类型为 int
auto y(27); // y 的类型为 int
auto z = {27}; // z 的类型为 std::initializer_list
auto w{27}; // w 的类型为 std::initializer_list
当用 auto 声明变量的初始化表达式是用 {} 括起来时,推导所得的类型就是 std::initializer_list,但std::initializer_list又是个模板:std::initializer_list<T>,如果 T 的类型推导失败那么代码就不能通过编译了。如果用 auto 声明的变量的初始化表达式是用 {} 括起来时,要意识到会发生两次类型推导,如下代码语句:
auto var1 = {2.5, 8.2, 5.1};
第一次类型推导是:由于是 auto 声明了变量 var1,所以 var1 的类型需要推导;
第二次类型推导是:var1 的类型会被推导为一个 std::initializer_list<T>,其中T的类型也需要推导。
对于花括号{}初始化表达式的处理方式,auto类型推导与模板类型推导唯一的不同之处。auto 声明的变量可以接受 {} 的初始化表达式,但是如果用 {}表达式来调用模板函数则可能会发生错误,具体要看模板函数的 ParamType 的形式:
//声明模板函数
template <typename T>
void f(T param);
//调用模板函数
f({12,78,0}); //错误,无法推导 T 的类型
//声明模板函数
template <typename T>
void f2(std::initializer_list<T> param);
//调用模板函数
f2({12,78,0}); // T 推导为 int, param 的类型为 std::initializer_list<int>
C++14中auto的使用
以上就是 关键字 auto 在C++11标准下的使用,但在C++14标准下,auto 还可以作为函数的返回值类型 以及 lambda表达式的形参类型,如下代码片段:
auto ReturnList() //auto 可以作为函数返回值类型使用
{
return {22, 11, 88}; // 错误,不能为 {22, 11, 88} 完成类型推导
}
std::vector<int> v;
auto getVec = [&v](const auto &tmpvec){
v = tmpvec;
};
getVec({22,11,88}); //调用lambda表达式, 但不能完成 {22, 11, 88} 的类型推导
有个关键的点需要记住:当 auto 用于函数的返回值类型时 或者用于lambda表达式的形参类型时,用的是模板函数的类型推导规则,并不是auto的类型推导规则;按模板函数的类型推导规则是不能接受{}的初始化表达式的,所以上面的示例代码是不能通过编译的。
以上就是关于auto 类型推导的全部内容,希望可以帮助到您。