int x; // 糟糕,忘记初始化了。x也许会被初始化为0也许不会,取决于上下文。
template<typename It> void dwim(It b, It e) { while (b != e) { // 真的假的?声明一个变量这么麻烦? typename std::iterator_traits<It>::value_type currValue = *b; } }
auto
的出现,以上这些问题都不复存在。auto
由initializer推断类型,因此不会出现变量未初始化问题。auto x1; // error! auto x2 = 0; // well-defined. template<typename It> void dwim(It b, It e) { while (b != e) { auto currValue = *b; // 舒服了 } } // 可以表示只由编译器知道的类型 auto derefUPLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) { return *p1 < *p2; };
auto
:auto derefUPLess = [](const auto& p1, const auto& p2) { return *p1 < *p2; };
std::function
。概念:C++11对函数指针概念的泛化,可以指向任何可调用的(callable)对象(注:如重载了operator()的类,俗称functor)。声明时在模板中给出指向函数的签名(signature):// 不使用auto的版本 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; }; // tmd, C++怎么会变成这个样子.jpg
且不论语法上的繁杂性,使用 std::function
和 auto
是不同的。auto
声明的闭包变量其类型就是自身,也只占用闭包需求的内存量。而 std::function
创建的是一个持有闭包对象的 std::function
对象,这通常会占用更多的内存,而且调用速度几乎肯定会更慢。本场比赛中,auto
完胜。
auto
的优势还不只如此。以下程序是你可能见过(或者写过的):
std::vector<int> v; unsigned sz = v.size();
v.size()
返回值的正式形式为 std::vector<int>::size_type
,然而很少开发者真正了解这一点,而往往认为用 unsigned
足够了。在32位机器上,两者大小相同;然而在64位机器上,前者是64位而后者是32位!size_t
的写法,这是没有问题的(见下图)。原书中没有提到这一点。std::unordered_map<std::string, int> m; m.insert(std::make_pair("123", 1)); m.insert(std::make_pair("234", 2)); for (const std::pair<std::string, int>& p : m) { ... // do something with p }
std::unordered_map
的键部分是 const
,因此哈希表中的元素不是 std::pair<std::string, int>
(声明的 p
的类型),而是 std::pair<const std::string, int>
!于是,编译器会想办法将后者转换为前者,并且还真的有一种方案(因此不会报错):把后者复制到一个临时对象,再降其引用赋给 p
。这绝对不是你想要的效果。用 auto
可以解决这一切问题:auto
推断出的类型可能不是你想要的一些情况。auto
可能会带来代码可读性上的问题。IDE的类型提示和命名风格良好的变量名称可以缓解这一问题。auto
只是你的一个可选工具。因此还是要根据实际场景合适地利用其强大功能。auto
变量必须被初始化,几乎能解决所有移植性和效率上的问题,还能简化代码重构的过程,同时能减少你的打字量。auto
类型的变量容易掉入Item 2和6所描述的坑中。