本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢!

博客已经迁移到这里啦

如果你已经读过item 1的模板类型推导,你已经知道大部分关于auto类型推导的知识了,因为,除了一种奇怪的情况外,auto类型推导和template类型推导是一样的。但是为什么会这样?template类型推导涉及模板和函数以及参数,但是auto没有处理这些东西。

是这样的,但是这没关系。从template类型推导到auto类型推导有一个直接的映射关系。这里有一个直接的算法来从template类型推导转换到auto类型推导。

item 1中,template类型推导是解释通用函数模板:

template<typename T>
void f(ParamType param);

并且这样简单调用:

f(expr);

对于f的调用,编译器用expr来推导TParamType的类型。

当一个变量用auto来声明时,auto扮演着的角色相当于template中的T,并且变量的类型相当于ParamType。直接举个例子会更简单一些:

auto x = 27;

这里,x的类型就是auto自身,另一方面,在这个声明中:

const auto cx = x;

类型是const auto,然后:

const auto& rx = x;

的类型是const auto&.为了推导x,cx,rx的类型,编译器表现地好像这里有template的声明,并且用初始化表达式调用相应的template:

tamplate<typename T>		//概念上的x的template类型推导
void func_for_x(T param); func_for_x(27); //概念上的调用:paramType的类型推导
//结果将会是x的类型,T的类型推导就是
//auto自身代表的类型 template<typename T>
void func_for_cx(const T param); func_for_cx(x); template<typename T>
void func_for_rx(const T& param); func_for_rx(x);

就像我说的,auto的类型推导,只有一个例外(我们马上就会讨论),其他的和template的类型推导是一样的。

item 1基于ParamType的不同情况,把template的类型推导分成三种情况。而使用auto来声明变量,变量类型取代了ParamType,所以这里也有三种情况:

  • 情况1:变量类型是指针或引用,但是不是universal引用。
  • 情况2:变量类型是universal引用。
  • 情况3:变量类型不是指针也不是引用。

我们已经看过情况1和情况3的例子了:

auto x = 27;		//情况3

const auto cx = x; 	//情况3

const auto& rx = x;	//情况1

你可以把情况2想象成这样:

auto&& uref1 = x;	//x是int,并且是左值,
//所以uref1的类型是int& auto&& uref2 = cx; //cx是const int,并且是左值,
//所以uref2的类型是const int& auto&& uref3 = 27; //27是int,并且是右值,
//所以uref3的类型是 int&&

item 1讨论了当类型是non-reference时,数组和函数退化成指针的情况,这样的情况也发生在auto类型推导中:

const char name[] = "R. N. Brigs";
//name的类型是 const char[13] auto arr1 = name; //arr1的类型是const char* auto& arr2 = name; //arr2的类型是const char()[13] void someFunc(int, double);
//声明一个函数,类型是void(int, double) auto func1 = someFunc;
//func1的类型是void(*)(int,double) auto& func2 = someFunc;
//func2的类型是void()(int, double)

就和你看到的一样,auto类型推导和template类型推导一样,他们本质上是硬币的两边。

除了一个情况下,他们是不同的。我们从观察一个情况开始,如果你想用27声明一个int变量,C++98允许你使用两种不同的语法:

int x1 = 27;
int x2(27);

C++11中,通过它对标准初始化的支持,增加了这些操作:

int x3 = {27};
int x4{27};

总之,四种语法都产生一个结果:一个值为27的int变量。

但是根据item 5的解释,声明变量时,用auto来替换确切的类型有几点好处,所以在上面的变量声明中,用auto来替换int是有好处的。直接的文本替换可以产生这样的代码:

auto x1 = 27;
auto x2(27);
auto x3 = {27};
auto x4{27};

这些声明都能编译,但是在用auto替换后,它们拥有了不同的解释。前两条语句确实声明了值为27的int变量。然而,后面两条语句,声明的变量类型为带有值为27的std::initializer_list

auto x1 = 27;	//类型是int, 值是27

auto x2(27);	//同上

auto x3 = {27};	//类型是 std::initializer_list<int>
//值是{27} auto x4{27}; //同上

产生这样的结果是由于auto的特殊的类型推导规则。当用初始化列表的形式来初始化auto声明的变量时,推导出来的类型就是std::initializer_list,下面这样的代码是错误的:

auto x ={1, 2, 3.0};	//错误! 不能推导
//std::initializer_list<T>中的T

就像注释里说的,这种情况下的类型推导将会失败,但是你需要知道这里其实产生了两种类型推导。一种就来自x5的auto类型推导。因为x5的初始化在花括号中,x5就被推导为std::initializer_list。但是 std::initializer_list是一个template。用T来实例化std::initializer_list意味着T的类型也必须被推导出来。这里发生的推导属于第二种类型推导:template类型推导。在这个例子中,这个推导失败了,因为初始化列表中的值不是同种类型的。

auto类型推导和template类型推导唯一的不同之处,就是对待初始化列表的不同做法。当用初始化列表来声明auto类型的变量时,推导出来的类型是std::initializer_list的一个实例。但是如果传入同样的初始化列表给template,类型推导将会失败,并且代码编译不通过:

auto x = {11, 23, 9};	//类型是std::initializer_list<int>

template<typename T>
void f(T param); f({11, 23, 9}); //错误!不能推导T的类型

然而如果你明确template的参数为std::initializer_list,template类型推导将成功推导出T:

template<typename T>
void f(std::initializer_list<T> initList); f({11, 23, 9}) //T被推导为int,并且initList的类型是
//std::initializer_list<int>

所以在auto和template的类型推导的唯一不同之处就是,auto假设初始化列表为std::initializer_list,但是template类型推导不这么做。

你可能想知道为什么auto类型推导对初始化列表有这么一个特别的规则,但是template类型推导却没有。我也想知道,可悲的是,我没有找到一个令人信服的解释。但是规则就是规则,并且这意味着你必须记住,如果你用auto声明一个变量并且用初始化列表来初始化它,那么被推导出来的类型就是std::initializer_list。你尤其要记住接受标准初始化---初始值在花括号中的思想理所当然的。(It’s especially

important to bear this in mind if you embrace the philosophy of uniform initialization—of enclosing initializing values in braces as a matter of course. )。C++11中的一个典型的错误就是,当你想声明一个其它变量时,意外地声明了一个std::initializer_list变量。这个陷阱导致了,有些开发者只在他们必须时才用大括号来初始化变量(什么时候才是必须的情况将在item 7中讨论)

对于C++11来说,故事已经完结了,但是对C++14来说,故事还将继续。C++14允许auto作为函数的返回值,并且在lambdas表达式中可能用auto来作为形参类型。然而,auto的这些用法使用的是template类型推导规则,而不是auto类型推导规则。所以返回一个初始化列表给auto类型的函数返回值将不能通过编译:

auto createInitList()
{
return {1, 2, 3}; //错误!不能推导{1,2,3}的类型
}

在C++14中,这对于作为lambdas表达式的参数声明的auto类型同样适用:

std::vector<int> v;
... auto resetV = [&v](const auto& newValue)
{ v = newValue; }; ... resetV({1, 2, 3}); //错误!不能推导{1,2,3}的类型
你要记住的事
  • auto类型推导常常和template类型推导一样,但是auto类型推导假设初始化列表为std::initializer_list,但是template类型推导不这么做
  • auto作为函数返回类型或lambdas表达式的形参类型时,默认使用template类型推导规则,而不是auto类型推导规则。

item 2: 理解auto类型的推导的更多相关文章

  1. Effective Modern C++ ——条款2 条款3 理解auto型别推导与理解decltype

    条款2.理解auto型别推导 对于auto的型别推导而言,其中大部分情况和模板型别推导是一模一样的.只有一种特例情况. 我们先针对auto和模板型别推导一致的情况进行讨论: //某变量采用auto来声 ...

  2. 现代C++之理解auto类型推断

    理解auto类型推断 上一篇帖子中讲述了模板类型推断,我们知道auto的实现原理是基于模板类型推断的,回顾一下模板类型推断: template <typename T> void f(Pa ...

  3. item 1:理解template类型的推导

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 一些用户对复杂的系统会忽略它怎么工作,怎么设计的,但是很高兴去知道它完成的一些事.通过这 ...

  4. Effective Modern C++ 条款2:理解auto型别推导

    在条款1中,我们已经了解了有关模板型别的推导的一切必要知识,那么也就意味着基本上了解了auto型别推导的一切必要知识. 因为,除了一个奇妙的例外情况,auto型别推导就是模板型别推导.尽管和模板型别推 ...

  5. 第2课 auto类型推导(1)

    第2课 auto类型推导(1) 一.auto类型推导 (一)与模板类型推导映射关系 1.auto类型推导与模板类型推导可以建立一一映射关系,它们之间存在双向的算法变换.auto扮演模板中T的角色,而变 ...

  6. 《Effective Modern C++》翻译--条款2: 理解auto自己主动类型推导

    条款2: 理解auto自己主动类型推导 假设你已经读过条款1关于模板类型推导的内容,那么你差点儿已经知道了关于auto类型推导的所有. 至于为什么auto类型推导就是模板类型推导仅仅有一个地方感到好奇 ...

  7. item 4: 知道怎么去看推导的类型

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 对于推导类型结果的查看,根据不同的软件开发阶段,你想知道的信息的不 ...

  8. Effective Modern C++翻译(3)-条款2:明白auto类型推导

    条款2 明白auto类型推导 如果你已经读完了条款1中有关模板类型推导的内容,那么你几乎已经知道了所有关于auto类型推导的事情,因为除了一个古怪的例外,auto的类型推导规则和模板的类型推导规则是一 ...

  9. auto类型推导

    引言 auto : 类型推导. 在使用c++的时候会经常使用, 就像在考虑STL时迭代器类型, 写模板的时候使用auto能少写代码, 也能帮助我们避免一些隐患的细节. auto初始化 使用auto型别 ...

随机推荐

  1. Django 信息

    Request information USER admin GET No GET data POST No POST data FILES No FILES data COOKIES Variabl ...

  2. Backbone.js学习之旅(一)

    前言 刚到粑粑公司,就学习各种框架,进行各种开发,为了纪念挥泪的青春,只好写下…… 希望能合您胃口^_^!!! The First(文件准备) backobone 强制依赖于 underscore.j ...

  3. Excel表格规范

    数据处理: 在数据进行分析使用时,需要去除原始数据中的脏数据,让统计数据均为有效数据: 统一表格的格式:去除空格.强制换行符 CLEAN()函数:是去除单元格中的特殊字符: TRIM()函数:去除单元 ...

  4. apache配置CA证书通过https通信

    Apache Httpd 2.2 实现https加密通讯 实际生产中CA证书一般是向一些专业认证的国际机构来进行申请的.我们会模拟使用OpenSSL生成的证书,来实现Apache的安全加密通讯,这与实 ...

  5. Linux 小知识翻译 - 「虚拟化技术」

    这次聊聊「虚拟化技术」. 虚拟化技术,有时简称为「虚拟化」,最近经常听人说它.但是却不太清楚它的意思.到底虚拟了什么东西?本来是用来干什么的? 有名的虚拟化软件要数 VMware 和 VirtualB ...

  6. Sring容器技术内幕之InstantiationStrategy类介绍

    引言 org.springframework.beans.factory.support.InstantiationStrategy负责根据BeanDefinition对象创建一个Bean实例.Spr ...

  7. vue文件在编辑器Sublime Text3中高亮

    编写代码时,代码在编辑器中显示高亮,一方面,在感官方面使人觉得很舒服:另一方面,还可以提高开发效率.下面简单介绍vue文件在Sublime Text3 中高亮的vue插件的安装方法: 第一步:安装Pa ...

  8. File类_删除一个带内容的目录_练习

    需求:删除一个带内容的目录 原理:必须从最里面往外删除需要深度遍历 import java.io.File; public class RemoveDirTest { public static vo ...

  9. 编译apache报APR not found

    系统环境: [vagrant@rs-2 download]$ cat /etc/redhat-release  CentOS release 5.6 (Final) [vagrant@rs-2 dow ...

  10. js常见错误类型

    (1)SyntaxError SyntaxError是解析代码时发生的语法错误 // 变量名错误 var 1a; // 缺少括号 console.log 'hello'); (2)ReferenceE ...