• 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    编译生成的代码

    #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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    分析

    情形1

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

    调用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()));
    
    • 1
    • 2

    可以看到的是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());
    
    • 1

    只不过是加了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);}
    
    • 1

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

    解决情况2->用强了

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

    生成如下:

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

    程序的运行结果

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

    在这里插入图片描述

  • 相关阅读:
    杰理AC632N蓝牙芯片iokey使用解析(通用MCU版)
    【含面试题】高并发场景下的接口调用优化
    idrac管理界面报错:RAC0508: 发生意外错误。
    解忧云SMS短信服务平台系统 短信发送系统 全解密完美版
    Spring Boot 中的Thymeleaf分页和排序示例
    单片机编程原则
    【算法】冒泡排序
    【无标题】
    英语连词总结
    Codesys数据类型(2.7):扩展数据类型之 别名 详解
  • 原文地址:https://blog.csdn.net/MMTS_yang/article/details/125503208