Table of Contents
1.auto的优点
1>避免未初始化变量
2>避免冗长的变量声明
3>具有直接持有闭包(closure)的能力
4>type shortcuts 类型捷径
2.auto的缺点
1>不适用invisible proxy class
2>Braced Initializer 类型推断
auto变量从它们的初始化式中推断它们的类型,所以它们必须被初始化。这意味着你可以免受很多未初始化变量带来的问题。
int x1; // potentially uninitialized
auto x2; // error! initializer required
auto x3 = 0; // fine, x's value is well-defined
因为auto使用类型推断,所以它可以表示某些只有编译器知道的类型:
// comparison func for Widgets pointed to by std::unique_ptrs
auto derefUPLess = [](const std::unique_ptr& p1, const std::unique_ptr& p2){
return *p1 < *p2;
};
在C++14中,甚至允许在lambda表达式参数中使用auto:
// values pointed to by anything pointer-like
auto derefLess = [](const auto& p1, const auto& p2){
return *p1 < *p2;
};
std::function是C++11标准库里的一个模板,它将函数指针通用化。函数指针只能指向函数,但是std::function对象可以指向任何callable对象(即可以像函数一样调用)。当你创建一个函数指针时,你必须给出函数的signature(即返回类型和形参表),同样地,当你创建std::function对象时,你也必须指定。
因为lambda表达式产生callable对象,所以闭包(closure)可以被存在std::function对象中。这意味着我们可以声明C++11版本的不使用auto的derefUPLess:
std::function&, const std::unique_ptr&)>
derefUPLess = [](const std::unique_ptr& p1, const std::unique_ptr& p2) {
return *p1 < *p2;
};
即使仅仅是将auto替换为类型名,使用std::function和auto仍然是不同的。
一个持有闭包(closure)的auto变量具有和闭包(closure)一样的类型,并且因此仅消耗闭包(closure)所需求的内存空间。
一个持有闭包(closure)的std::function变量的类型是std::function模板的一个具现(instantiation),并且它对于任意的函数signature都有固定的内存空间。这个内存空间的大小也许并不满足闭包(closure)的需求,所以std::function的构造函数可能会申请堆内存来存储闭包(closure)。因此,std::function对象通常会比auto对象消耗更多的内存空间。
另外,实现细节禁用inline,会导致间接地函数调用。因此,通过std::function对象调用闭包(closure)几乎肯定会比通过auto对象调用慢。
总之,std::function方法会比auto方法消耗更多空间且执行更慢,并且std::function方法还可能产生out-of-memory的异常。
std::vector v;
unsigned sz1 = v.size();
auto sz2 = v.size(); // sz2's type is std::vector::size_type
v.size()的返回类型是std::vector
std::unordered_map m;
...
for(const std::pair& p : m)
{
... // do something with p
}
for(const auto& p : m)
{
... //as before
}
看出第一种写法有什么问题吗?std::unordered_map的key是const的,所以在hash table中的std::pair不是std::pair
这两个例子说明显式给出类型会导致你不期望的隐式转换。如果使用auto作为变量的类型,就不必担心你声明的类型与用来初始化变量的表达式的类型不一致的问题。
虽然使用auto声明变量会带来很多好处,但是有时auto的类型推断也会带来你不期望的行为。
std::vector features(const Widget& w);
Widget w;
...
bool highPriority = features(w)[5];
...
processWidget(w, highPriority);
这个例子没有问题,可以正常工作。但如果用auto声明highPriority,就会带来未定义的行为。
auto highPriority = features(w)[5];
...
processWidget(w, highPriority); // undefined behavior!
为什么会有未定义的行为?这就要牵涉到vector
1>首先,使用auto的话,highPriority的类型就不是bool了,而是std::vector
2>std::vector
3>features(w)会返回一个临时的std::vector
std::vector
一些代理类被设计成显式行为,如std::shared_ptr和std::unique_ptr。其他的代理类则被设计成隐式地,如std::vector
通常来说,隐式的代理类不适合用auto。所以要避免如下的使用:
auto someVar = expression of "invisible" proxy class type;
如果auto变量使用花括号的初始化式子,变量的类型将被推断为std::initializer_list。
auto x = {11, 23, 9}; // x's type is std::initializer_list
C++14允许auto用于函数的返回类型,并且lambda的参数也可以用auto声明。但是这些对auto的使用并不会将花括号的初始化式子推断为std::initializer_list,因此编译时会报错:
auto createInitList()
{
return {1, 2, 3}; // error: can't deduce type for {1, 2, 3}
}
auto resetV = [&v](const auto& newValue) { }; // C++14
resetV({1, 2, 3}); // error! can't deduce type for {1, 2, 3}
整理自:《Effective Modern C++》第二章 auto