Effective Modern C++ Item 5

Prefer auto to explicit type declarations

C++11 开始,auto变量从初始化表达式中推导出类型,这意味着我们必须显式地初始化一个变量。

1
2
3
4
5
int x1;                         //潜在的未初始化的变量

auto x2; //错误!必须要初始化

auto x3 = 0; //没问题,x已经定义了

而且省去了很多打代码的麻烦。考虑以下两段执行相同操作的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename It>           //对从b到e的所有元素使用
void dwim(It b, It e) //dwim(“do what I mean”)算法
{
while (b != e) {
typename std::iterator_traits<It>::value_type currValue = *b;
}
}

template<typename It> //如之前一样
void dwim(It b,It e)
{
while (b != e) {
auto currValue = *b;
}
}

使用Item2所述的auto类型推导技术,它甚至能表示一些只有编译器才知道的类型:

1
2
3
4
5
6
7
8
9
10
// C++11
auto derefUPLess =
[](const std::unique_ptr<Widget> &p1, //用于std::unique_ptr
const std::unique_ptr<Widget> &p2) //指向的Widget类型的
{ return *p1 < *p2; }; //比较函数
// C++14
auto derefLess =
[](const auto& p1, //被任何像指针一样的东西
const auto& p2) //指向的值的比较函数
{ return *p1 < *p2; };

但是,有人觉得不需要使用auto声明局部变量来保存一个闭包,可以使用std::function对象。

std::function是一个C++11标准模板库中的一个模板,它泛化了函数指针的概念。std::function可以指向任何可调用对象。但创建std::function对象非常麻烦,而且还可能需要更多内存来存储闭包(堆上)。

1
2
3
4
5
std::function<bool(const std::unique_ptr<Widget> &,
const std::unique_ptr<Widget> &)>
derefUPLess = [](const std::unique_ptr<Widget> &p1,
const std::unique_ptr<Widget> &p2)
{ return *p1 < *p2; };

使用auto除了可以避免未初始化的无效变量,省略冗长的声明类型,直接保存闭包外,还有一个好处是可以避免类型快捷方式有关的问题。

1
2
std::vector<int> v;
unsigned sz = v.size();

v.size()的标准返回类型是std::vector<int>::size_type。在Windows 32-bit std::vector<int>::size_typeunsigned是一样的大小,但是在Windows 64-bitstd::vector<int>::size_type是64位,unsigned是32位,这就会出问题。

还有一个例子,考虑以下代码:

1
2
3
4
5
std::unordered_map<std::string, int> m;
for(const std::pair<std::string, int>& p : m)
{
//用p做一些事
}

std::unordered_mapkeyconst的,所以std::pair的类型是std::pair<const std::string, int>。由于类型不一致,编译器会通过拷贝m中的对象创建一个临时对象,然后把p的引用绑定到这个临时对象上。在每个循环迭代结束时,临时对象将会销毁。

使用auto可以避免这些很难被意识到的类型不匹配的错误:

1
2
3
4
for(const auto& p : m)
{
//如之前一样
}

这样不仅代码更简洁,而且确保与std::unordered_map中真实pair的引用“绑定”, 而并不只是一个临时拷贝的引用。

重点

  • auto 声明变量必须初始化, 通常它可以避免一些移植性和效率性的问题, 并可以简化重构, 且比明确指定类型的变量声明打更少的代码。
  • auto声明变量会受到Item2和Item2中描述的陷阱的影响。