Effective Modern C++ 条款2:理解auto型别推导
在条款1中,我们已经了解了有关模板型别的推导的一切必要知识,那么也就意味着基本上了解了auto型别推导的一切必要知识。
因为,除了一个奇妙的例外情况,auto型别推导就是模板型别推导。尽管和模板型别推导打交道的是模板、函数和形参,auto和它们秋毫无犯,但并不影响上面的结论成立。
在条款1中,我们用来解释模板型别推导的函数模板形如:
template<typename T>
void f(ParamType param);
而一次调用形如:
f(expr); //以某表达式调用f
在f的调用语句中,编译器会利用expr来推导T和ParamType的型别。
当某变量采用auto来声明时,auto就扮演了模板中的T这个角色,而变量的型别饰词则扮演的是ParamType的角色。
所以,除了一种例外情况(马上要讨论),和为模板推导型别一模一样。
条款1根据ParamType的特征,即一般形式的函数模板中param的型别饰词,将模板推导分为三种情况。而在采用auto进行变量声明中,型别饰词取代了ParamType,所以也存在三种情况:
情况1:型别饰词是指针或引用,但不是万能引用;
情况2:型别饰词是万能引用;
情况3:型别饰词既非指针也非引用。
情况1和情况3,如下:
auto x = ; //情况3(x既非指针也非引用) const auto cx = x; //情况3(cx同样既非指针也非引用) const auto& rx = x; //情况1(rx是个引用,但不是万能引用)
情况2的运作也如期望一致:
auto&& uref1 = x; //x的型别是int,且是左值,
//所以uref1的型别是int& auto&& uref2 = cx; //cx的型别是const int,且是左值,
//所以uref2的型别是const int& auto&& uref2 = ; //27的型别是int,且是右值,
//所以uref3的型别是int&&
条款1中数组和函数名称在非引用型别饰词的前提下退化成指针,同样的结论也适用于auto型别推导:
const char name[] = "Hello World"; //name的型别是const char[12] auto arr1 = name; //arr1的型别是const char* auto& arr2 = name; //arr2的型别是const char(&)[12] void someFunc(int ,double); //someFunc是个函数,
//其型别是void(int ,double)
auto func1 = someFunc; //func1的型别是void(*)(int, double) auto& func2 = someFunc; //func2的型别是void(&)(int, double)
auto型别推导和模板型别推导的运作是类似的。
下面我们讨论只有一个不同之处。我们观察一下,若要声明int,并将其初始化为值27,C++98中有两种可选语法:
int x1 = ;
int x2();
而C++11为了支持统一初始化,增加了下面的语法规则:
int x3 = {  };
int x4{  };
共计四种语法,然而结果却殊途同归:得到一个值为27的int。
采用auto声明变量,相比采用规定型别声明变量更具优势,所以将上面变量声明中的int替换成auto。直截了当的文本替换的到下面代码:
auto x1 = ;
auto x2();
auto x3 = { };
auto x4{ };
这些声明都能通过编译,但结果却与一开始并不全部相同。前面两个语句却是仍然声明了一个型别为int,值为27的变量,而后面两个语句,却声明了这么一个变量,其型别为std::initializer_list<int>,且含有单个值为27的元素!
auto x1 = ; //型别是int,值是27
auto x2(); //同上
auto x3 = { }; //型别是std::initializer_list<int>,值是{ 27 }
auto x4{ }; //同上
这是有关auto的一条特殊的型别推导规则所致。当用于auto声明变量的初始化表达式是使用大括号括起来时,推导所得的型别就属std::initializer_list<int>。
这么一来,如果型别推到失败(例如,大括号里的值型别不一),则代码就通不过编译:
auto x5 = { , , 3.0};   //错误!
                          //推导不出std::initializer_list<T>中的T
正如注释所指出的那样,这种情况下型别推导会失败,但重点在于,要意识到这里发生了两种型别推导。第一种源于auto的使用:x5的型别需要推导。而x5的初始化表达式是用大括号括起的,所以x5必须推导为一个std::initializer_list。但std::initializer_list是个模板,它要根据某个型别T产生实例型别std::initializer_list<T>,而这就意味着T的型别也必须被推导出来。而后一次推导就落入了第二种型别推导,即模板型别推导的范畴了。在该例中,推导会失败,因为大括号括起的初始化表达式中的值型别不一。
对于大括号初始化表达式的处理方式,是auto型别推导和模板型别推导的唯一不同之处。当采用auto声明的变量使用大括号初始化表达式进行初始化时,推导所得的型别是std::initializer_list的一个实例型别。但是,如果想对应的模板传入一个同样的初始化表达式,型别推导就会失败,代码不能通过编译:
auto x = { , ,  };     //x的型别是std::initializer_list<int>
template<typename T>       //带有形参的模板
void f(T param);        //与x的声明等价的声明式
f({ , , });       //错误!无法推导T的型别
所以,auto和模板型别推导真正的唯一区别在于,auto会假定用大括号括起来的初始化表达式代表一个std::initializer_list,但模板型别推导却不会。
不过,若指定模板中的param为std::initializer_list<T>,则在T的型别未知时,模板型别推导机制会推导出T应有的型别:
template<typename T> //带有形参的模板
void f(std::initializer_list<T> param); f({ , , }); //T的型别推导为int
//从而initList的型别std::initializer_list<int>
关于C++11,上面所说的就是全部了。但是关于C++14,还有别的要说。C++14允许使用auto来说明函数返回值需要推导,而且C++14中的lambda式也会在形参声明中用到auto。然而,这些auto用法是在使用模板型别推导而非auto型别推导。所以带有auto返回值的函数若要返回一个大括号括起的初始化表达式,是通不过编译的:
auto createInitList()
{
return { , , }; //错误:无法为{ 1, 2, 3}完成型别推导
}
同样地,用auto来制定C++14中lambda式的形参型别时,也不能使用大括号括起的初始化表达式:
std::vector<int> v;
... auto resetV =
[&v](const auto& newValue) { v = newValue; }; // C++14 ... resetV( {,,});//错误!无法为{1,2,3}完成型别推导
要点速记:
1、在一般情况下,auto型别推导和模板型别推导时一模一样的,但是auto型别推导会假定用大括号括起的表达式代表一个std::initializer_list,但模板型别推导却不会。
2、在函数返回值或lambda式的形参中使用auto,意思是使用模板型别推导而非auto型别推导。
Effective Modern C++ 条款2:理解auto型别推导的更多相关文章
- Effective Modern C++ ——条款2  条款3 理解auto型别推导与理解decltype
		条款2.理解auto型别推导 对于auto的型别推导而言,其中大部分情况和模板型别推导是一模一样的.只有一种特例情况. 我们先针对auto和模板型别推导一致的情况进行讨论: //某变量采用auto来声 ... 
- Effective Modern C++ ——条款6 当auto型别不符合要求时,使用带显式型别的初始化物习惯用法
		类的代理对象 其实这部分内容主要是说明了在STL或者某些其他代码的容器中,在一些代理类的作用下使得最后的返回值并不是想要的结果. 而他的返回值则是类中的一个容器,看下面的一段代码: std::vect ... 
- Effective Modern C++ 条款4:掌握查看型别推导结果的方法
		采用何种工具来查看型别推导结果,取决于你在软件开发过程的哪个阶段需要该信息.主要研究三个可能的阶段:撰写代码阶段.编译阶段.运行时阶段. IDE编译器 IDE中的代码编译器通常会在你将鼠标指针选停止某 ... 
- Effective Modern C++  条款3:理解decltype
		说起decltype,这是个古灵精怪的东西.对于给定的名字或表达式,decltype能告诉你该名字或表达式的型别.一般来说,它告诉你的结果和你预测的是一样的.不过,偶尔它也会给出某个结果,让你抓耳挠腮 ... 
- 《Effective Modern C++》翻译--条款2: 理解auto自己主动类型推导
		条款2: 理解auto自己主动类型推导 假设你已经读过条款1关于模板类型推导的内容,那么你差点儿已经知道了关于auto类型推导的所有. 至于为什么auto类型推导就是模板类型推导仅仅有一个地方感到好奇 ... 
- Effective Modern C++  条款1:理解模板型别推导
		成百上千的程序员都在向函数模板传递实参,并拿到了完全满意的结果,而这些程序员中却有很多对这些函数使用的型别是如何被推导出的过程连最模糊的描述都讲不出来. 但是当模板型别推导规则应用于auto语境时,它 ... 
- Effective Modern C++ ——条款5 优先选择auto,而非显式型别声明
		条款5 对于auto ,他的好处不仅仅是少打一些字这么简单. 首先在声明的时候, 使用auto会让我们养成初始化的习惯: auto x;//编译不通过必须初始化. 再次对于auto而言,它可以让我们定 ... 
- [Effective Modern C++] Item 2. Understand auto type deduction - 了解auto类型推断
		条款二 了解auto类型推断 基础知识 除了一处例外,auto的类型推断与template一样.存在一个直接的从template类型推断到auto类型推断的映射 三类情况下的推断如下所示: // ca ... 
- item 2: 理解auto类型的推导
		本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 如果你已经读过item 1的模板类型推导,你已经知道大部分关于au ... 
随机推荐
- <Django> MVT三大块之Models(模型)
			1.ORM(对象-关系-映射)---面向对象,不需要面向SQL语句 根据对象的类型生成表结构 将对象.列表的操作,转化成SQL语句 将SQL语句查询的结果转化成对象.列表 目的:实现数据模型与数据库的 ... 
- 多重背包 /// 单调队列DP oj1943
			题目大意: em.... 就是多重背包 挑战340页的东西 ...自己的笔记总结总是比较乱的 重点:原始的状态转移方程中 更新第 i 种物品时 重量%w[i] 的值不同 则它们之间是相互独立的: 1- ... 
- bigcolorpicker 颜色拾取器插件——例
			参考:http://bigui4.sinaapp.com/picker/colorpicker.html 效果: html: <!DOCTYPE html> <html> &l ... 
- java、jsp导出excel功能备份
			问题踩坑: ajax请求不能下载文件 必须这样: <a href="/media">点击下载Excel</a> 或者 location.href = '/m ... 
- (转)Unity3D手游开发实践
			作者:吴秦出处:http://www.cnblogs.com/skynet/本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦(包含链接). (转)& ... 
- postgresql数据库安装后的pgadmin4中无法加载本地连接解决办法
			postgresql 在安装最后一步提示the database cluster initialisation failed, 而后点开pgadmin4发现如下图所示 经过百度搜索找出问题原因, 由于 ... 
- cache方法用于查询缓存操作,也是连贯操作方法之一。
			cache方法用于查询缓存操作,也是连贯操作方法之一. cache可以用于select.find和getField方法,以及其衍生方法,使用cache方法后,在缓存有效期之内不会再次进行数据库查询操作 ... 
- Django 操作Cookie与Session
			目录 Cookie Session Django中操作Cookie 基本操作 基于cookie的登录装饰器 Django中操作Session 基本操作 Session流程解析 基于session的登录 ... 
- svn使用方法以及使用教程
			一.什么是svnSVN是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS.CVS,它采用了分支管理系统,它的设计目标就是取代CVS. 二.svn的下载安装下载地址:https: ... 
- 15_K-近邻算法之入住位置预测
			案例:本次大赛的目的是预测一个人想签入到哪个地方.对于本次比赛的目的,Facebook的创建一 个人造的世界,包括位于10公里的10平方公里超过10万米的地方.对于一个给定的坐标,你的任务是返回最有可 ... 
