Item6:当auto推导的类型不符合要求时,使用显式类型初始化习惯用法
个人认为原著写的非常难懂,所以精简总结如下:
auto与代理类的问题
当使用auto进行类型推导时,如果表达式返回的是代理类,auto会推导出代理类型而不是被代理的实际类型,可能导致未定义行为。
例如:
1 | std::vector<bool> features(const Widget& w); |
这段代码没有什么问题,但是如果把highPriority从显式类型改成auto:
1 | auto highPriority = features(w)[5]; // 危险!推导出std::vector<bool>::reference |
原因:
std::vector<bool>内部使用紧凑结构存储(每个bool占1位)- 内存最小寻址单位是字节,单个 “位” 没有独立的内存地址;
operator[]无法返回bool&(不能对单个位取引用),因此返回一个代理类std::vector<bool>::referenceauto不会自动把代理类转换成bool,而是直接推导成std::vector<bool>::reference类型。
因此需要显式声明其为bool类型:
1 | auto highPriority = static_cast<bool>(features(w)[5]); // 安全! |
如何识别出代理类
头函数或函数签名会反映出其存在:
1 | namespace std { |
非同寻常的返回值类型:如:std::vector<T>::operator[]通常返回T&,如果返回类型不是T&,可能是代理类。
常见的代理类场景:
std::vector<bool>的相关操作:位无独立地址,无法返回引用。- 表达式模板(用于优化数值计算):临时对象多,计算效率低。
- 某些智能指针的实现:内存访问不安全/权限难控。
- 延迟求值(lazy evaluation)系统:提前计算可能无效。
其他应用场景
1. 精度控制
1 | double calcEpsilon(); |
还可以应用于计算随机访问迭代器的容器中的某个元素下标。
2. 类型转换强调
1 | double d = 0.7; |
3. 表达式模板的代理
1 | Matrix m1, m2, m3, m4; |
总结
- “隐形”的代理类型可以导致
auto根据初始化表达式推导出“错误的”类型。 - 带显式类型的初始化习惯用法强制
auto推导出你想要的类型。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 肖恩的博客!
评论
