2. Auto

24 阅读3分钟

auto可以存储类型,但是有时会出现一些错误,甚至会有性能上面的问题,但是我们应该拥抱它,引导它产生正确结果

1. 优先考虑auto而非显式声明

  1. 显式声明存在问题
  • 可能会没有初始化
  • 嵌套类型声明复杂
  • 有些编译期才知道的类型,无能为力,如lambda
  1. 定义lambda时,编译器会自动生成一个闭包类型(只有编译器才知道其类型名),并实例化该类型的对象
  2. std::function 是一个 类型擦除的通用函数包装器,可以存储任意可调用对象(函数指针、Lambda、仿函数等),但需要会有多余的内存开销、性能开销,因此更推荐使用auto来接收
  • 可能有意想不到的类型不匹配的错误
std::unordered_map<std::string, int> m;
…
//`std::pair`的类型不是`std::pair<std::string, int>`,
//而是`std::pair<const std::string, int>`
//下面代码不会报错,而是产生临时变量再赋值
for(const std::pair<std::string, int>& p : m)
{
    …                                   //用p做一些事
}
  1. 尽管auto会对源码可读性产生问题,但是有些情况下,抽象类型(如智能指针)比精确类型更有帮助,而抽象类型可以借助变量名来存储;同时IDE也会帮助我们显示精确类型
  2. auto能很好的帮我们重构代码,如一个函数返回类型被声明为int,但是后来你认为将它声明为long会更好,调用它作为初始化表达式的变量会自动改变类型,但是如果你不使用auto你就不得不在源代码中挨个找到调用地点然后修改它们。
  3. 正如Item26讨论的,auto类型的变量可能会踩到一些陷阱。

6. auto推导若非己愿,使用显式类型初始化惯用法

  1. 代理类:为了模拟或增管其他类,比如显式代理类智能指针、隐式代理类std::vector<bool>::reference

vector<bool>operator[]返回类型,因为该容器内部值是用位图存储的,所以不能像其他类型容器直接返回引用,而需要一个代理类

  1. 隐形代理类无法与auto共存,防止写出auto Var = 隐形代理类表达式
Widget w;
std::vector<bool> features(const Widget& w);
…
auto highPriority = features(w)[5];  
// 推导出reference隐式代理类
// 该对象中有指针指向features返回的临时变量std::vector<bool>中的位图以及偏移量
// 所以后续在使用对象是未定义的processWidget(w, highPriority);    //未定义行为!
  1. 显式类型初始化惯用法使用auto声明一个变量,然后对表达式强制类型转换(cast)得出你期望的推导结果。std::vector<bool> features(const Widget& w);
  2. 应用这个惯用法不限制初始化表达式产生一个代理类。它也可以用于强调你声明了一个变量类型,它的类型不同于初始化表达式的类型。
double calcEpsilon();         //返回double
auto ep = static_cast<float>(calcEpsilon()); //使用float精度就可以了