《Effective Modern C++》学习笔记之条款六:当auto推导的型别不符合要求时,使用带显示型别的初始化

虽然auto用起来确实好处多多,到那时也并不是万能的,在有些场景下,我们就不得不使用带显示型别的初始化。例如如下代码:

std::vector<bool> features() {
     std::vector<bool> temp;
     for(int i = 0;i < 10; ++i) {
         temp.push_back(true);
     }
     
     return temp;
}

auto result = features()[5];//错误,将导致未定义行为

bool result2 = features()[5];//正确

上述代码很简单,一个函数返回一个vector对象,里面放了10个true,然后使用auto接收第6个(从0开始计算)值,我们理所当然的认为,此时result的值为bool类型,但是实际上并不是,std::vector<T>对于bool类型有着特殊处理且仅仅对bool类型有特殊处理,其返回值为并不是bool,而是std::vector<bool>::reference类型。之所以会会出现这种结果是因为STL对std::vector<bool>的operator[]函数做过特殊,用一种压缩的方式表示其持有的bool元素,即,每个bool元素用一个比特位来表示。正常来说,std::vector<T>的operator[]函数返回的应该为T&,但是C++却禁止比特位的引用,所以std::vector<bool>的operator[]函数返回的是std::vector<bool>::reference类型,且std::vector<bool>::reference类型做了一个向bool类型的隐式转换,正是因为这种隐式转换的存在,我们才可以使用使用bool类型来接收std::vector<bool>::reference。

上面std::vector<bool>的情况实际是使用了代理类的场景std::vector<bool>::reference是bool的代理,可能你会问,我们怎么知道某些库函数是否使用了代理呢?方法很简单,看源码。源码是可以看出来,但是如果每次使用一个库函数都要去看一下源码,无疑是降低了我们的开发效率,所以这里就介绍了一种一劳永逸的方法:使用带显示型别的初始化,说直白点就是在赋值前,进行显示强制类型转换。不仅对于std::vector<bool>的operator[]函数,当用auto接收某些表达式的值时,最好也使用带显示型别的初始化,例如上面代码的auto语句,我们可以改成这样:

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

auto sum = static_cast<MyType>(m1 + m2 + m3);

auto index = static_cast<int>(d * c.size());

要点速记

  • 隐形代理型别可能导致auto根据初始化表达式推导出的型别是错误的
  • 带显示型别的初始化,可以强制auto推导出你想要的型别

猜你喜欢

转载自blog.csdn.net/Chiang2018/article/details/114047768