auto

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 = "隐形"代理型别表达式;

所以我们的问题是什么呢?

我们的问题是那些代码中包含有“隐形”代理型别。

posted @ 2020-12-21 17:05  cyssmile  阅读(316)  评论(0编辑  收藏  举报