这次是针对 Effective Modern C++ Item2的学习笔记。
这次学习 auto 类型的自动推导,在 auto 类型推导方法上,除了一种场景外,在Item1 中学习的模板类型推导方法都可以试用。回顾下模板的类型推导:
templatevoid f(ParamType param); f(expr); // call f with some expression
在调用点 f,编译器使用 expr 去推导出 T 和 ParamType 的类型。
对于使用 auto 声明的变量,auto 对应 T,变量的类型描述符对应 ParamType。如下例子,rx 的类型描述符是 const auto&,把 x 理解成函数 f 调用的参数 expr。
const auto& rx = x
对应模板类型推导根据 ParamType 类型分3种场景进行推导,使用 auto 对变量进行申明的类型推导,也根据变量的类型描述符分3种场景进行推导:
- Case 1:类型描述符是一个指针或者引用,但不是万能引用。Case 2:类型说明符是一个万能引用。Case 3:类型说明符既不是指针也不是引用。
Item1 的方法适用这3种场景,下面举例说明。
Case 1:类型描述符是一个指针或者引用,但不是万能引用。
int x = 27; const int cx = x; const int& rcx = x; auto& y = x; // y 的类型为 int& auto& y1 = cx; // y1 的类型为 const int& auto& y2 = rcx; // y2 的类型为 const int& auto& y3 = rcx; // y2 的类型为 const int&
Case 2:类型说明符是一个万能引用。
int x = 27; const int cx = x; const int& rcx = x; auto&& y = 27; // y 的类型为 int&& auto&& y1 = x; // y1 的类型为 int& auto&& y2 = cx; // y2 的类型为 const int& auto&& y3 = rcx; // y3 的类型为 const int&
Case 3:类型说明符既不是指针也不是引用。
int x = 27; const int cx = x; auto y = 27; // y 的类型为 int auto y1 = x; // y1 的类型为 int auto y2 = cx; // y2 的类型为 int const auto y3 = x; // y3 的类型为 const int const auto y4 = cx; // y4 的类型为 const int
Item1 中也讨论了数组和函数名退化成指针的情况,也同样适用与 auto 的类型推导:
const char name[] = "R. N. Briggs"; auto y1 = name; // y1 类型为 const char* auto& y2 = name; // y2 类型为 const char (&) [13] void someFunc(int, double); auto f1 = someFunc; // f1 类型为 void (*)(double, int) auto& f2 = someFunc; // f2 类型为 void (&)(int, double)
特殊场景:初始化列表 std::initial izer_list
对于变量初始化,如下:
int x1 = 27; int x2(27); int x3 = { 27 }; int x4{ 27 };
x3 和 x4 使用的是初始化列表的方式进行初始化,x1~x4 的类型都是int类型。但是,Item5 中将会解释为什么使用 auto 申明特定类型的变量会具有优势,这里将 int 换成 auto:
auto x1 = 27; // type is int, value is 27 auto x2(27); // type is int, value is 27 auto x3 = { 27 }; // type is std::initializer_list, // value is { 27 } auto x4{ 27 }; // type is std::initializer_list , // value is { 27 }
x1 和 x2 还是 int 类型,但是 x3 和 x4 却是 std::initial izer_list
但是,下面的初始化方式会失败:
auto x5 = { 1, 2, 3.0 }; // error! can't deduce T for std::initializer_list
因为这里实际上包含了两种类型推导,首先 x5 的类型被推导成 std::initializer_list,由于 std::initializer_list 是一个模板,然后必须为 std::initializer_list
这是 auto 类型推导和模板类型推导的区别,传递这样的初始化列表给模板将导致推导失败:
templatevoid f(T param); f({ 11, 23, 9 }); // error! can't deduce type for T
但是,如果你指定了模板参数类型为 std::initializer_list
templatevoid f(std::initializer_list initList); f({ 11, 23, 9 }); // T deduced as int, and initList's type is std::initializer_list
在 Item3 中你将看到 C++14 允许 auto 作为函数返回值,并且可以被推导。然后那是利用了模板推导,并不是 auto 推导,因此 auto 作为函数返回值时不允许返回一个大括号初始化列表,将会编译失败:
auto createInitList() { return { 1, 2, 3 }; // error: can't deduce type for { 1, 2, 3 } }
对于 lambda 函数也是一样:
std::vectorv; ... auto resetV = [&v](const auto& newValue) { v = newValue; }; // C++14 ... resetV({ 1, 2, 3 }); // error: can't deduce type for { 1, 2, 3 }
最后,总结 auto 推导如下:
auto 类型推导除了大括号初始化列表方式外,和模板类型推导方法一致。模板类型推导不支持 std::initializer_list。函数返回值为 auto 时,实际是使用模板推导,不是 auto 类型推导。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)