条款6.当auto推导的类型不符合要求时,使用带显式类型的初始化物习惯用法

当auto推导的类型不符合要求时,使用带显式类型的初始化物习惯用法

举个例子,假设有一个函数接受一个Widget并返回一个std::vector<bool>,其中每一个bool元素都代表着Widget是否提供一种特定功能;

Widget w;

std::vector<bool> features(const Widget& w);

//第5个比特代表的意思是:Widget是否具有高优先级
bool highPriority = features(w)[5];	//w具有高优先级么?

processWidget(w, highPriority);		//按照w的优先级来处理

上面这段代码没什么问题。

如果我们把highPriority从显式类型改成auto

auto highPriority = features(w)[5];		//w具有高优先级么?

processWidget(w,highPriority);	//未定义行为

为什么会出现未定义行为,因为在使用了auto之后,highPriority的类型不再是bool了。尽管从概念上来说,std::vector<bool>应该持有的是bool类型的元素,但是std::vector<bool>operator[]的返回值并不是容器中的一个元素的引用(对于其他所有形参类型而言,std::vector::operator[]都返回这样的值,单单bool是个例外),它返回的是std::vector<bool>::reference类型的对象

之所以整出个std::vector<bool>::reference,是因为std::vector<bool>做过特化,用了一种压缩形式表示其持有的bool元素,每个bool元素用一个比特来表示。这种做法给std::vector<bool>operator[]带来了一个问题,因为按理来说,std::vector<T>operator[]应该返回一个T&,然而C++中禁止比特的引用。既然不能返回一个bool&std::vector<bool>operator[]转而返回了一个表现的像bool&的对象。std::vector<bool>类型的对象就要在所有能用bool&的地方保证它们也能用实现这个效果的原理是,std::vector<bool>::reference做了一个向bool的隐式类型转换

bool highPriority = features(w)[5];		//显式声明highPriority的类型

这里,features返回了一个std::vector<bool>对象,然后针对该对象执行operator[]。然后,operator[]返回一个std::vector<bool>::reference类型的对象,该对象紧接着被隐式转化为一个初始化highPriority所需的bool对象。

auto highPriority = features(w)[5];

features返回了一个std::vector<bool>对象,然后针对该对象执行operator[]。然后,operator[]返回一个std::vector<bool>::reference类型的对象,auto会把highPriority的类型推导成std::vector<bool>::reference。这么一来,highPriority的值就完全不可能是features所返回的std::vector<bool>对象的第5个比特。

在后一种情况下,highPriority的值取决于std::vector<bool>::reference的实现。有一种实现让对象含有一个指针,指向一个机器字,该机器字持有那个被引用的比特,再加上基于那个比特对应的字的偏移量。

对features的调用会返回一个std::vector<bool>类型的临时对象。该对象没有名字。我们成为temp。针对temp执行operator[],返回一个std::vector<bool>::reference类型的对象,该对象含有一个指向机器字的指针,该机器字在一个持有temp所管理的那样比特的数据结构中,还要加上第5个比特所对应的机器字的偏移量。由于highPriority是std::vector<bool>::reference对象的一个副本,所以highPriority也含有一个指向temp中的机器字的指针,还要加上第5个比特所对应的机器字的偏移量。

在表达式结束处,temp会被析构,因为它是一个临时对象。结果,highPriority会含有一个空悬指针,最终导致调用processWidget时出现未定义行为

processWidget(w,highPriority);	//未定义行为,highPriority含有空悬指针

std::vector<bool>::reference是个代理类的实例。所谓代理类,就是指为了模拟或增广其他类型的类。

一个普遍的规律是,“隐形”代理类无法与auto和平共处

所以,你要防止写出这样的代码:

auto someVar = "隐形"代理类型表达式

对于代理类,使用auto时,只要进行一次类型转换就可以避免上述问题

auto highPriority = static_cast<bool>(features(w)[5]);

此处,feature(w)[5]仍然返回一个std::vector<bool>::reference对象,但是强制类型转换将这个表达式的类型转换成了bool,从而auto就将highPriority推导成了该类型。

要点速记

  • “隐形”的代理类型可以导致auto根据初始化表达式推导出“错误的”类型
  • 带显式类型的初始化物习惯用法强制auto推导出你想要的类型
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值