Effective Modern C++[实践]->auto类型推导不符合要求时,使用强转

当auto推导出非预期类型时应当使用显式的类型初始化

概述

条款6:当auto推导出非预期类型时应当使用显式的类型初始化

  1. std::vector<T>std::deque<T>·的operator[]常常返回一个T&`,
  2. std::vector<bool>的不同
    std::vector<bool> 特化定义 std::vector<bool>::reference 为可公开访问的嵌套类。
    std::vector<bool>::reference 代理访问 std::vector<bool> 中单个位的行为。
    std::vector<bool>::reference 的基础使用是提供能从 operator[] 返回的左值。
    任何通过std::vector<bool>::reference发生的对vector 的读或写,会潜在地读或写整个底层的 vector

有关vector<bool>的更多示例详见vector 类,此处我们重点关注《条款6》

分析

代码:

#include <iostream>
#include <vector>
using namespace std;

class widget{
  public:
	vector<bool> bVector={false,true,true,false,true,true};
    int other = 0;
};
vector<bool> get(const widget &w){
    return w.bVector;
}
void process(widget w,bool boolValue){
	cout<<"boolValue= "<<boolValue<<endl;
}
int main() {
  widget w;
  //情况1
  bool v = get(w)[2];
  process(w,v);
  //情况2
  auto v1 = get(w)[2];
  process(w,v1);
  //解决方法
  auto v2 = static_cast<bool>(get(w)[2]);
  process(w,v2);
}

编译生成的代码

#include <iostream>
#include <vector>
using namespace std;

class widget{
  public: 
  std::vector<bool, std::allocator<bool> > bVector;
  int other;
  // inline widget(const widget &) noexcept(false) = default;
  // inline ~widget() noexcept = default;
  // inline constexpr widget() noexcept(false) = default;
};
std::vector<bool, std::allocator<bool> > get(const widget & w)
{
  return std::vector<bool, std::allocator<bool> >(w.bVector);
}
void process(widget w, bool boolValue)
{
  std::operator<<(std::cout, "boolValue= ").operator<<(boolValue).operator<<(std::endl);
}
int main()
{
  widget w = widget();
  bool v = static_cast<bool>(get(w).operator[](2).operator bool());
  process(widget(w), v);
  std::_Bit_reference v1 = std::_Bit_reference(get(w).operator[](2));
  process(widget(w), static_cast<bool>(v1.operator bool()));
  bool v2 = static_cast<bool>(get(w).operator[](2).operator bool());
  process(widget(w), v2);
  return 0;
}

分析

情形1

 bool v = static_cast<bool>(get(w).operator[](2).operator bool());
 process(widget(w), v);

调用operator[]后返回的是std::_Bit_reference,又调用了operator bool()返回了bool的引用,这是没有问题的。
在这里插入图片描述

情形2

生成的代码如下:

  std::_Bit_reference v1 = std::_Bit_reference(get(w).operator[](2));
  process(widget(w), static_cast<bool>(v1.operator bool()));

可以看到的是auto关键字推断出的是一个std::_Bit_reference ,这已经不是一个bool 引用或者bool变量了,而是一个嵌套在vector<bool>里的一个代理类,这样编译是没有问题的,但是结果却不一样了,至于为啥要这么干,原因大致如下:

  1. 因为bool占用一个字节,标准库为了节省内存,改用bit来表示;
  2. 因为operator[]需要返回一个内部元素的引用,但是没办法对一个bit进行引用;
  3. 为了让返回的类型统一,无论是bool类型,还是其它类型;

注意结果不一致这句话 , 再看下情形1生成的代码

bool v = static_cast<bool>(get(w).operator[](2).operator bool());

只不过是加了std::_Bit_reference v1这么一个中间对象,就不一样了吗OMG?随即查到了大神关于operator[]的解析
下图原文连接
在这里插入图片描述

再结合汇编代码
在这里插入图片描述

得出了个人感想: 这个vector<bool>在调用完释放掉了,仅剩一个引用对象std::_Bit_reference在孤芳自赏,再去调用operator bool()。此时应该crash但是没有,继续分析下std::_Bit_reference

在这里插入图片描述

于是决定加断点分析下:
加断点,发现*_M_p的指向0,而_M_mask的值缺依旧在,至此觉得是vector<bool>释放导致了该指针指向了空,再看opertaor bool()的实现大致如下:

operator bool()const {return !!(*_M_p&_M_mask);}

至此,大致明白了,不崩也正常,但值肯定就不对了。

解决情况2->用强了

 auto v2 = static_cast<bool>(get(w)[2]);
 process(w,v2);

生成如下:

 bool v2 = static_cast<bool>(get(w).operator[](2).operator bool());
 process(widget(w), v2);

程序的运行结果

若不看结果,你可能对此条款有诸多怀疑,原谅我把结果写在最后

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-西门吹雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值