Item 2: Understand auto type deduction.

Item 2: Understand auto type deduction.,第1张

Item 2: Understand auto type deduction.

Item 2: Understand auto type deduction

这次是针对 Effective Modern C++ Item2的学习笔记。

这次学习 auto 类型的自动推导,在 auto 类型推导方法上,除了一种场景外,在Item1 中学习的模板类型推导方法都可以试用。回顾下模板的类型推导:

template
void f(ParamType param);

f(expr); // call f with some expression

在调用点 f,编译器使用 expr 去推导出 TParamType 的类型。

对于使用 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 };

x3x4 使用的是初始化列表的方式进行初始化,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 }

x1x2 还是 int 类型,但是 x3x4 却是 std::initial izer_list 类型,并包含一个元素 27。这是变量申明 auto 类型推导的特殊之处:当使用 auto 申明一个变量 ,并且使用大括号的方式初始化变量,变量的类型推导为 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 实例化一个 T,也就是说 T 也要被推导。这里里表里包含了两种数据类型,T 推导失败。

这是 auto 类型推导和模板类型推导的区别,传递这样的初始化列表给模板将导致推导失败:

template
void f(T param);

f({ 11, 23, 9 }); // error! can't deduce type for T

但是,如果你指定了模板参数类型为 std::initializer_list ,模板类型推导将推导出 T

template
void 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::vector v;
...
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 类型推导。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5718977.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存