第3课 auto类型推导(2)

一、使用auto的优势

(一)避免使用未初始化变量

(二)可简化变量/对象类型的声明

(三) 在某些场合无法判断出类型时,可用auto自动推导(如lambda表达式)

(四)可自适应类型,避免隐式类型转换或显式指定类型可能出现的类型错误,增加代码的可移植性。

【编程实验】优先使用auto

#include <iostream>
#include <vector>
#include <map>
#include <functional>
#include <unordered_map>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr; //辅助类模板,用于打印T的类型
template <typename T>
void printType(string s)
{
cout << s << " = " << type_id_with_cvr<T>().pretty_name() << endl;
} class Widget
{
int mArea;
public:
bool operator<(const Widget& w) {
return mArea < w.mArea;
}
}; //简化变量/对象类型的声明
template<typename It> //It为迭代器类型
void dwim(It b, It e) //dwim = do what I mean,做我所想
{
while (b != e) { //遍历从b到e范围内的元素
//1. 冗余、繁琐的写法
//typename std::iterator<It>::value_type //萃取迭代器所指的元素类型
// currValue = *b; //2. 使用auto自动推导元素的类型
auto currValue = *b;
//...
}
} //在某些场合无法判断出类型时,可用auto自动推导
class A
{
public:
static int func()
{
return ;
}
}; class B
{
public:
static double func()
{
return 1.5f;
}
}; template<typename T>
auto test() //C++14,返回值将根据T::func()类型的不同而不同,但无法事先判断出来!
{
return T::func();
} int main()
{
//1. 避免使用未初始化变量
//auto x; //编译失败,x必须被初始化。 //2. 可简化变量/对象类型的声明
std::map<double, double> resMap; //2.1 繁琐的写法
//std::map<double, double>::iterator it = resMap.begin();
//for(; it != resMap.end(); ++it){
// //do something
//} //2.2简化写法
for (auto it = resMap.begin(); it != resMap.end(); ++it) {
} //3. 在某些场合无法判断出类型时,可用auto自动推导(如lambda表达式)
//3.1 C++11写法
auto lam1 = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) {
return *p1 < *p2;
};
//3.2 C++14写法(形参可以用auto推导)
auto lam2 = [](const auto& p1, const auto& p2)
{
return *p1 < *p2;
};
printType<decltype(lam1)>("lam1");
printType<decltype(lam2)>("lam2"); //3.2 用std::function来保存lambda(书写同样繁琐!)
std::function<bool(const std::unique_ptr<Widget>&, const std::unique_ptr<Widget>&)> //须先声明函数的类型,繁琐!
func = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) {
return *p1 < *p2;
};
printType<decltype(func)>("func"); //3.3 无法事先确定类型时,可用auto自动推导。
cout << test<A>() << endl; //1: 返回值为int
cout << test<B>() << endl; //1.5 返回值为double //4. 可自适应类型,增加代码的可重用性和移植性。
//4.1 莫贪图方便,写出“快捷类型”,如以下的unsigned
std::vector<int> vec;
unsigned sz = vec.size(); //vec.size()返回值的类型为std::vector<int>::size_type与unsigned不完全相同
//unsigned在32位和64位的Windows系统上均是32位的整数。但
//std::vector<int>::size_type在32位和64位Windows分别是32位和64位。
//以上代码从32位移植到64位时,可能出现不一致行为!
auto sz2 = vec.size(); //自动推导类型:std::vector<int>::size_type,在32位和64位上行为表现一致! printType<decltype(sz)>("sz");
printType<decltype(sz2)>("sz2"); //4.2 化解无心之错引发的类型不匹配
std::unordered_map<std::string, int> m; //unorder_map内部使用std::pair<key,value>存储键值对,且key为const类型
//using value_type = pair<const _Kty, _Ty>;(参考自VC++2019源码)
printType<std::unordered_map<string, int>::value_type>("std::unordered_map<string, int>::value_type"); for (const std::pair<std::string, int>& p : m) { //这里不小心将key写成std::string。(正确应为const string)
//do something //由于这个疏忽,尽管p是个引用,但由于类型不匹配,将导致m中的
} //每个元素产生一个临时std::pair对象并复制给p,从而造成性能损失。 //正确的解法
for (const auto& p : m) {
//do something
}
return ;
}
/*输出结果:
lam1 = class <lambda_7300d527c50ad8fd230cc9023161ed22>
lam2 = class <lambda_e2c5f09f6886e25bc08911f7b03dc529>
func = class std::function<bool __cdecl(class std::unique_ptr<class Widget,struct std::default_delete<class Widget> > const &,class std::unique_ptr<class Widget,struct std::default_delete<class Widget> > const &)>
1
1.5
sz = unsigned int
sz2 = unsigned int
std::unordered_map<string, int>::value_type = struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,int>
*/

二、auto推导存在的问题

(一)存在问题

①auto声明的变量使用大括号初始化表达式进行初始化时,推导出的类型是initializer_list<T>类型。但当auto用在函数返回值推导时,采用的是模板推导规则而不是auto规则,即无法将大括号初始化表达式自动推导为initializer_list类型。

②“隐形”的代理类与auto无法和平共处。(代理类,比如std::vector<bool>::reference,由于std::vector<bool>采用压缩算法来存储bool值,其operator[]返回值不是普通的T&,而是前述的代理类,通过这个类提供的可以实现从代理类到bool值的隐式转换)。应避免写出如下代码auto var1 = “隐形”代理类表达式,而应该显式转换如,auto var1 = static_cast<bool>(“隐形”代理类表达式);

(二)解决方案:显式指定类型或显式强制类型转换

【编程实验】auto推导存在的问题

#include <iostream>
#include <vector>
#include <map>
#include <type_traits> #include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr; //辅助类模板,用于打印T的类型
template <typename T>
void printType(string s)
{
cout << s << " = " << type_id_with_cvr<T>().pretty_name() << endl;
} class Widget
{
public:
}; //features函数,用于记录Widget对象特性(如第5个比特代表是否具有高优先级)
//(注意:std::vector<bool>是个特化模板,采用压缩算法来存储bool。如每个
//比特位用0或1代表一个bool值),其operator[]返回的是std::vector<bool>::reference类型,而不是bool&。
std::vector<bool> features(const Widget& w)
{
return { true, false, true, false, true, false, true};
} //根据优先级处理Widget
void processWidget(const Widget& w, bool priority)
{
//do something
} double calcEpsilon() //返回容量差
{
return ;
}
int main()
{
//实验1:std::vector<bool>及隐形代理类
using ref_t = std::vector<bool>::reference;
using val_t = decltype(std::vector<bool>()[]); cout << std::is_same<ref_t, val_t>::value << endl; //1, 查看std::vector<bool>::reference == operator[] ? Widget w; //1.1 正确读取优先级
bool highPriority = features(w)[]; //std::vector<bool>::reference到bool的隐式转换
cout << highPriority << endl; //ok,这里打印0,表示false;
processWidget(w, highPriority); //根据优先级处理Widget //1.2 错误方式
auto highPriority2 = features(w)[]; //返回的是std::vector<bool>::reference,也完全不可能是std::vector<bool>对象
//的第5比特了,因为highPriority2可能存在空悬指针(dangling pointer)。原因如下:
//features()函数调用后,产生一个std::vector<bool>的临时对象(称之temp对象),
//operator[]返回std::vector<bool>::reference对象(称之ref对象)。
//假设std::vector<bool>中的有个保存这些bool压缩形式的比特数据结构,再假设
//std::vector<bool>::reference中有一指针指向该结构,加上5的偏移量就我们要的优先级
//由于auto推导,ref对象会被复制给highPriority2对象,但当features函数结束后,temp对象
//析构,那块比特数据结构也被释放,所以highPrirotity2对象中的那指针会成为空悬指针。 //processWidget(w, highPriority2); //hightPriority2会隐式转为bool,函数调用成功。但hightPriority2中空悬指针存在,会导致
//未定义行为。
//1.3 仍使用auto推导的解决方案:
auto highPriority3 = static_cast<bool>(features(w)[]); // highPriority3为bool类型。
processWidget(w, highPriority3); //实验2:带显式类型转换强制auto推导出所需的类型
//double calcEpsilon();
float ep = calcEpsilon(); //从double到float的隐式转换。缺点,隐式转换,可以并非所需。
auto ep2 = static_cast<float>(calcEpsilon()); //显示转换
cout << "ep = " << ep << endl;
cout << "ep2 = " << ep2 << endl; vector<int> vec = { ,,,,, };
double d = 0.5f; //用0.0至1.0之间的值来表示和容器起始处有多远,0.5表示中间位置。 int index = d * vec.size(); //隐式转换,意图不明显。
auto index2 = static_cast<int>(d * vec.size()); //显示转换
cout << "index = " << index << endl;
cout << "index2 = " << index2 << endl;
}
/*输出结果:
1
0
ep = 0
ep2 = 0
index = 3
index2 = 3
*/

第3课 auto类型推导(2)的更多相关文章

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

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

  2. 第4课 decltype类型推导

    第4课 decltype类型推导 一.decltype类型推导 (一)语法: 1.语法:decltype(expr),其中的expr为变量(实体)或表达式 2.说明: ①decltype用于获取变量的 ...

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

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

  4. auto类型推导

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

  5. 类型推导:函数模板与auto

    1.从函数模板谈起 函数模板的类型推导机制是在c++98时代就有的,auto的类型推导机制与其基本一致,所以先理解函数模板类型推导. 函数模板可以用如下代码框架表示: #template<typ ...

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

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

  7. item 2: 理解auto类型的推导

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 如果你已经读过item 1的模板类型推导,你已经知道大部分关于au ...

  8. c++11——auto,decltype类型推导

    c++11中引入了auto和decltype关键字实现类型推导,通过这两个关键字不仅能够方便的获取复杂的类型,而且还能简化书写,提高编码效率.     auto和decltype的类型推导都是编译器在 ...

  9. 模板类型推导、auto推导

    effective modern c++ 果然是神书,干货满满,简单记录下. item1 模板推倒 典型的模板函数 temlate<class T> void fn(ParamType p ...

随机推荐

  1. 【java】查看Java字节码文件内容的方法+使用javap找不到类 解决方法

    研究synchronized底层实现,涉及到查看java字节码的需要 前提是,你的PC已经成功安装了JDK并别配置了环境变量. ==========查看方法========= 一.javap查看简约字 ...

  2. javascript中的异步操作以及Promise和异步的关系

    https://segmentfault.com/a/1190000004322358 Promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和强大 https://se ...

  3. moodle3.7中文语言包

    Moodle官方有中文语言包,但是还有没有翻译的,为了提高用户体验,可以将部分未翻译的应用在Moodle网站管理中自己修改. 具体步骤: 先确定需要修改的关键字,也就是网站中没有翻译成中文的文字 在c ...

  4. lift提升图

    Lift图衡量的是,与不利用模型相比,模型的预测能力“变好”了多少,lift(提升指数)越大,模型的运行效果越好. TP:划一个阈值后的正样本. P:总体的正样本. 在模型评估中,我们常用到增益/提升 ...

  5. springmvc上传图片到Tomcat虚拟目录

    一.简介 通过把文件上传到tomcat的虚拟目录,实现代码和资源文件分开. 二.环境 spring+springmvc+mybatis 三.代码实现 1.导入文件上传的jar <dependen ...

  6. EF自动创建数据库步骤之三(自定义数据库初始器)

    EF自动创建数据库需要我们告诉数据库如何进行初始化:如创建表后是否需要插入一些基础数据,是否 需要创建存储过程.触发器等.还有就是EF有三种初始化方式(参见下面三个类): DropCreateData ...

  7. JSP JSTL

    JSTL是Sun给JSP制定的一套标准标签库,JS代表JSP,TL即Tag Library. JSTL是一套很古老的标签库了,很多东西都不再适用,这里只介绍几个常用的标签. 使用JSTL需下载添加以下 ...

  8. angular6 使用tooltip

    准备 为了使用tooltip,可以安装ng-bootstrap组件,它包含两个依赖: angular:5.0以上版本: bootstrap css: 4.0版本. 安装好bootstrap之后,更新a ...

  9. C语言实现归并排序

    #include<stdio.h> #define MAXN 100 //A[p,q] A[q+1,r]是两个有序数组,想办法把他们结合成一个有序数组 void merge(int A[] ...

  10. django的form 登录组件

    1. 了解form 组件的原理 1.建立好form组件 class>>> 2.了解需要先is_valid() 判断是否可以取值 成功 form.cleaned_data 查看成功认证 ...