Skip to content

Commit

Permalink
Update auto.md
Browse files Browse the repository at this point in the history
  • Loading branch information
archibate authored Dec 27, 2024
1 parent c6bbf5f commit 8119e85
Showing 1 changed file with 178 additions and 2 deletions.
180 changes: 178 additions & 2 deletions docs/auto.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,187 @@ TODO

## 变量声明为 `auto`

```cpp
int i = 0;
```

### `auto` 声明万物的好处

#### 避免复读类型

> {{ icon.fun }} 人类的本质是复读机。
```cpp
QSlider *slider = new QSlider();
```

```cpp
auto slider = new QSlider();
```

TODO

#### 模板编程产生的超长类型名喧宾夺主

在 C++98 时代,仅仅只是保存个迭代器作为变量,就得写一长串:

```cpp
std::map<std::string, int> tab;
std::map<std::string, int>::iterator it = tab.find("key");
```

这踏码的类型名比右侧的表达式都长了!

> {{ icon.fun }} 哮点解析:张心欣的第三条腿比另外两条腿都长。
有了 `auto` 以后,无需复读类型名和繁琐的 `::iterator` 废话,自动从右侧 `find` 函数的返回值推导出正确的类型。

```cpp
std::map<std::string, int> tab;
auto it = tab.find("key");
```

#### 避免未初始化

因为 `auto` 规定必须右侧有赋初始值(否则无法推导类型)。

所以只要你的代码规范能一直使用 `auto` 的话,就可以避免未初始化。

众所周知,读取一个未初始化的变量是未定义行为,C/C++ 程序员饱受其苦,小彭老师也好几次因为忘记初始化成员指针。

例如,你平时可能一不小心写:

```cpp
int i;
cout << i; // 未定义行为!此时 i 还没有初始化
```

但是如果你用了 `auto`,那么 `auto i` 就会直接报错,提醒你没有赋初始值:

```cpp
auto i; // 编译出错,强制提醒你必须赋初始值!
cout << i;
```

你意识到自己漏写了 `= 0`!于是你写上了初始值,编译才能通过。

```cpp
auto i = 0;
cout << i;
```

可见,只要你养成“总是 `auto`”的好习惯,就绝对不会忘记变量未初始化,因为 `auto` 会强制要求有初始值。

#### 自动适配类型,避免类型隐式转换

假设你有一个能返回 `int` 的函数:

```cpp
int getNum();
```

有多处使用了这个函数:

```cpp
int a = getNum();
...
int b = getNum() + 1;
...
```

假如你哪天遇到牢板需求改变,它说现在我们的 `Num` 需要是浮点数了!

```cpp
float getNum();
```

哎呀,你需要把之前那些“多处使用”里写的 `int` 全部一个个改成 `float`

```cpp
float a = getNum();
...
float b = getNum() + 1;
...
```

如果漏改一个的话,就会发生隐式转换,并且只是警告,不会报错,你根本注意不到,精度就丢失了!

现在“马后炮”一下,如果当时你的“多处使用”用的是 `auto`,那该多好!自动适应!

```cpp
auto a = getNum();
...
auto b = getNum() + 1;
...
```

无论你今天 `getNum` 想返回 `float` 还是 `double`,只需要修改 `getNum` 的返回值一处,所有调用了 `getNum` 的地方都会自动适配!

> {{ icon.fun }} 专治张心欣这种小计级扒皮牢板骚动反复跳脚的情况,无需你一个个去狼狈的改来改回,一处修改,处处生效。
#### 统一写法,更可读

```cpp
std::vector<int> aVeryLongName(5);
```
```cpp
auto aVeryLongName = std::vector<int>(5);
```

TODO

#### 强制写明字面量类型,避免隐式转换

有同学反映,他想要创建一个 `size_t` 类型的整数,初始化为 3。

```cpp
size_t i = 3; // 3 是 int 类型,这里初始化时发生了隐式转换,int 转为了 size_t
i = 0xffffffffff; // OK,在 size_t 范围内(64 位编译器)
```

如果直接改用 `auto` 的话,因为 `3` 这个字面量是 `int` 类型的,所以初始化出来的 `auto i` 也会被推导成 `int i`

虽然目前初始只用到了 `3`,然而这位同学后面可能会用到 `size_t` 范围的更大整数存入,就存不下了。

```cpp
auto i = 3; // 错误!auto 会推导为 int 了!
i = 0xffffffffff; // 超出 int 范围!
```

由于 C++ 是静态编译,变量类型一旦确定就无法更改,我们必须在定义时就指定号范围更大的 `size_t`

为了让 `auto` 推导出这位同学想要的 `size_t` 类型,我们可以在 `3` 这个字面量周围显式写出类型转换,将其转换为 `size_t`

> {{ icon.tip }} 显式类型转换总比隐式的要好!
```
auto i = (size_t)3; // 正确
```

这里的类型转换用的是 C 语言的强制类型转换语法 `(size_t)3`,更好的写法是用括号包裹的 C++ 构造函数风格的强制类型转换语法:

```
auto i = size_t(3); // 正确
```

看起来就和调用了 `size_t` 的“构造函数”一样。这也符合我们前面说的统一写法,类型统一和值写在一起,以括号结合,更可读。

> {{ icon.detail }} 顺便一提,`0xffffffffff` 会是 `long` (Linux) 或 `long long` (Windows) 类型字面量,因为它已经超出了 `int` 范围,所以实际上 `auto i = 0xffffffffff` 会推导为 `long i`。字面量类型的规则是,如果还在 `int` 范围内(0x7fffffff 以内),那这个字面量就是 `int`;如果超过了 0x7fffffff 但不超过 0xffffffff,就会变成 `unsigned int`;如果超过了 0xffffffff 就会自动变成 `long` (Linux) 或 `long long` (Windows) ;超过 0x7fffffffffffffff 则变成 `unsigned long` (Linux) 或 `unsigned long long` (Windows) ——这时和手动加 `ULL` 等后缀等价,无后缀时默认 `int`,如果超过了 `int` 编译器会自动推测一个最合适的。
如果需要其他类型的变量,改用 `short(3)``uint8_t(3)` 配合 `auto` 不就行了,根本没必要把类型前置。

#### 避免语法歧义

TODO

### `auto` 的小插曲:初始化列表

TODO

## 返回类型 `auto`

C++11 引入的 `auto` 关键字可以用作函数的返回类型,但它只是一个“占位”,让我们得以后置返回类型,并没有多大作用,非常残废。
C++11 引入的 `auto` 关键字可以用作函数的返回类型,但它只是一个“占位”,让我们得以后置返回类型,并没有多大作用,所以 C++11 这版的 `auto` 非常残废。

```cpp
auto f() -> int;
Expand All @@ -22,7 +198,7 @@ int f();
> {{ icon.detail }} 当初引入后置返回类型实际的用途是 `auto f(int x) -> decltype(x * x) { return x * x; }` 这种情况,但很容易被接下来 C++14 引入的真正 `auto` 返回类型推导平替了。
但是 C++14 引入了函数**返回类型推导**`auto` 才算真正意义上能用做函数返回类型它会自动根据函数中的 `return` 表达式推导出函数的返回类型。
终于,C++14 引入了函数**返回类型推导**`auto` 才算真正意义上能用做函数返回类型它会自动根据函数中的 `return` 表达式推导出函数的返回类型。

```cpp
auto f(int x) {
Expand Down

0 comments on commit 8119e85

Please sign in to comment.