auto
1、减少代码量
2、与std::function相比,更快。
使用auto声明functonal时,auto声明存储一个壁报变量,与该闭包是同一型别的,内存量也和该闭包是一样的。
而使用std::function的时候声明的是一个std::function的实例,不管是什么样都占用固定的尺寸的内存。如果该内存不够用的时候
std::function的构造函数都会分配堆内存来存储闭包。所以std::function比auto更慢。
3、与容器使用时 (unordered_map)
// g++ -o tt test_auto.cpp -std=c++11
// ./tt
#include <stdio.h>
#include <string>
#include <unordered_map>
#define PRINTLINE \
do { \
printf("\n"); \
} while (0);
int main() {
std::unordered_map<int, std::string> map1;
for (auto i = 0; i < 10; i++) {
map1.insert(std::make_pair(i, std::move(std::to_string(i))));
}
for (const std::pair<int, std::string> &p : map1) {
auto ptr = &p.second;
printf("%p\t", ptr);
}
PRINTLINE
for (const auto &p : map1) {
auto ptr = &p.second;
printf("%p\t", ptr);
}
}
这段代码的运行结果
我们发现上下两行的地址并不相同
这是unordered_map的键是const的
所以说在for (const std::pair<int, std::string> &p : map1)这个循环中会将
map1本身的std::pair<const int, std::string> 转换成 std::pair<int, std::string>,所以说这个中间就会有一个复制操作。
那么实际上每次都是将p赋值给一个临时变量,然后每次循环结束就会被释放。当你不知道具体类型或者已经知道(但是理解不正确)的时候就最好使用auto.
比如这里如果你明确pair的类型const std::pair<const int, std::string>&p
结果也是一样的
#include <stdio.h>
#include <iostream>
#include <string>
#include <unordered_map>
#define PRINTLINE \
do { \
printf("\n"); \
} while (0);
int main() {
std::unordered_map<int, std::string> map1;
for (auto i = 0; i < 10; i++) {
map1.insert(std::make_pair(i, std::move(std::to_string(i))));
}
for (const std::pair<const int, std::string> &p : map1) {
auto ptr = &p.second;
printf("%p\t", ptr);
}
PRINTLINE
for (const auto &p : map1) {
auto ptr = &p.second;
printf("%p\t", ptr);
}
PRINTLINE
}
"代理"型别导致auto推导结果和预期不一样
“代理模式”大家学设计模式都应该知道。常见的代理类有智能指针(std::shared_ptr、std::unique_ptr),std::bitset::reference, std::vector
::reference等。
比如这么一个例子
class Widget { // ... };
std::vector<bool> features(const Widget& w);
用features来返回一个vector
代表w是否满足有个功能。
然后我们选择返回vector
中第五个特质。
bool hightPriority = features(w)[5];
然后我们写一个函数来处理这个特质
processWidget(w, hightPriority);
这样写没问题,但是我如果将这里的显式声明的改成auto
auto hightPriority = features(w)[5];
processWidget(w, hightPriority); // 未定义行为
虽然代码可以编译通过,但是会出现未定义行为。
通常来说std::vector::operator[]会返回一个T&,但是对于std::vector
的情况是一个例外。
它返回的是一个std::vector
::reference型别的对象(这是一个嵌套在std::vector
中的类)。
这是为什么呢?
之所以要弄一个std::vector
::reference,是因为std::vector
做过特化
:使用一种压缩方式来表示每一个bool元素,即用一个bit来表示一个bool元素。然而c++中却禁止bit引用。
怎么说呢?就是本来operator[]返回的是一个bool&,但是实际上返回的却是一个bool&的reference。
那么为什么前一种写法可以呢?
因为前一种写法中,做了一个std::vector
::reference 到std::vector
的隐式转换。
虽然我们使用features返回的是一个std::vector
::reference,但是做了隐式转换后就变成std::vector
.
那为什么后一种写法不行呢?
后一种写法中features返回的是一个std::vector
::reference ,使用 auto后,hightPriority,对std::vector
::reference
进行operator[],就不是std::vector
中第5个元素,而是std::vector
::reference中第五个bit
结论
“隐形”代理类和auto无法和平共处。这种类的对象往往会设计维持到单个语句内存在,所以如果要创建这种类的变量,往往就违反了基本库设计的假定前提。
所以我们要防止写出这样的代码
auto someVar = "隐形"代理型别表达式;
所以我们的问题是什么呢?
我们的问题是那些代码中包含有“隐形”代理型别。