您的位置:首页 > 编程语言 > C语言/C++

《Effective Modern C++》读书笔记(2) -- auto类型推导(auto type deduction)

2017-05-18 12:52 866 查看
模板类型推导涉及模板(template),函数(functinos)和参数(parmeters),但是auto并没有处理处理这些问题。

auto类型推导

auto作为C++11新添加的特性之一,很强大,即便有些许不足,但是也是瑕不掩瑜。

对于auto而言,它的强大之处在于类型推导(type deduce),因此既然是推导,那么它就不能像普通的变量一样,只声明不定义,例如:

int a;                  // 声明变量a为一个整形,没有定义

auto a;             // 错误,无法推导出变量a的类型

auto a = 1;         // 正确,a的类型为整形


即使auto在这方面有些许欠缺,但依然不能阻止我们使用它。

和模板类型推导相似,这里也有三种情况

类型说明符是一个指针(pointer)或者引用(reference),但不是一个通用引用(universal reference)

类型说明符是一个通用引用(universal reference)

类型说明符既不是指针也不是引用

类型说明符是一个指针(pointer)或者引用(reference),但不是一个通用引用(universal reference)

例如:

int a = 27;
auto& rx = a;           // rx是一个引用,它的类型为int&


模板类型推导中,讨论了一个数组或者一个函数名传递的推导,在auto中也是一样的

例如:

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)


类型说明符是一个通用引用(universal reference)

当我们写下:

auto x = 27;
const auto cx = x;
const auto& rx = x;

auto&& uref1 = x;
auto&& uref2 = cx;
auto&& uref3 = 27;


对于:

1. uref1

x的类型是int并且是左值(lvalue),所以uref1的类型是int&

uref2

cx的类型是const int并且是左值(lvalue),所以uref2的类型是const int&

uref3

27的类型是int并且是右值(rvalue),所以uref3的类型是int&&

对于引用,这里有一个重要的知识点:引用折叠(至于具体的内容,可以先自行查阅资料,之后会讲这点),这里简单举个栗子。

例如:

Widget w;                // 声明一个不知道是什么的类


当我们这样使用auto的时候,

auto&& w1 = w;          // 这时候就会发生引用折叠


当我们声明

auto&& w1 = w;


即用一个左值初始化w1,因此编译器就会将Widget&这个类型推到给auto说明符,替换之后,代码变成:

Widget& && w1 = w;              // 这里有三个(&)符号


通过引用折叠,最后变成

Widget& w1 = w;


所以w1的类型为左值引用,即Widget&

回到上面说的,为什么uref3的类型为int&&呢,重新看一下代码

auto&& uref3 = 27;


27是一个右值,因此编译器就会将int&&这个类型推到给auto说明符,替换之后,代码变成:

int&& && uref3 = 27;        // 这里有四个(&)符号


通过引用折叠,最后变成

int&& uref3 = 27;


答案就出来了

类型说明符既不是指针也不是引用

先说下在C++11之前,如果声明一个变量为整形并为它赋初值,有两种方法:

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 };
autox4{ 27 };


对于前面两种,结果是:声明一个整形变量,其值为27

对于后面两种,结果是:声明一个类型为

std::initializer_list<int>


的容器,包含一个值为27的元素

很奇怪不是嘛?记住就行,这是auto的一个特殊推导规则。

例如,我们可以这样写:

auto x5 = {1, 2, 3};

auto x6 = {1, 2, 3.0};          // 错误


x6错误是由于std::initializer_list是一个模板类,前面讲了模板的推导,需要推导TParamType的类型

这里产生了歧义,即intdouble类型。无法确定唯一的T

auto,lambda和函数返回类型

在C++11中,这些都是迷!!例如,你不能这样写:

#include <iostream>

template <typename T>
auto func(T i)
{
return i;
}

int main(int argc, char const *argv[])
{
int a = func<int>(2);
std::cout << a << std::endl;
return 0;
}


只能改成:

#include <iostream>

template <typename T>
auto func(T i) -> decltype(i)
{
return i;
}

int main(int argc, char const *argv[])
{
int a = func<int>(2);
std::cout << a << std::endl;            // 输出:2
return 0;
}


但在C++14中,你可以使用第一种方法。C++14允许auto推导函数的返回类型,lambda也可以在参数列表中使用auto。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: