6
6
7
7
## 变量声明为 ` auto `
8
8
9
+ ``` cpp
10
+ int i = 0 ;
11
+ ```
12
+
13
+ ### 用 ` auto ` 声明万物的好处
14
+
15
+ #### 避免复读类型
16
+
17
+ > {{ icon.fun }} 人类的本质是复读机。
18
+
19
+ ``` cpp
20
+ QSlider *slider = new QSlider();
21
+ ```
22
+
23
+ ``` cpp
24
+ auto slider = new QSlider();
25
+ ```
26
+
27
+ TODO
28
+
29
+ #### 模板编程产生的超长类型名喧宾夺主
30
+
31
+ 在 C++98 时代,仅仅只是保存个迭代器作为变量,就得写一长串:
32
+
33
+ ``` cpp
34
+ std::map<std::string, int > tab;
35
+ std::map<std::string, int >::iterator it = tab.find(" key" );
36
+ ```
37
+
38
+ 这踏码的类型名比右侧的表达式都长了!
39
+
40
+ > {{ icon.fun }} 哮点解析:张心欣的第三条腿比另外两条腿都长。
41
+
42
+ 有了 ` auto ` 以后,无需复读类型名和繁琐的 ` ::iterator ` 废话,自动从右侧 ` find ` 函数的返回值推导出正确的类型。
43
+
44
+ ``` cpp
45
+ std::map<std::string, int > tab;
46
+ auto it = tab.find(" key" );
47
+ ```
48
+
49
+ #### 避免未初始化
50
+
51
+ 因为 ` auto ` 规定必须右侧有赋初始值(否则无法推导类型)。
52
+
53
+ 所以只要你的代码规范能一直使用 ` auto ` 的话,就可以避免未初始化。
54
+
55
+ 众所周知,读取一个未初始化的变量是未定义行为,C/C++ 程序员饱受其苦,小彭老师也好几次因为忘记初始化成员指针。
56
+
57
+ 例如,你平时可能一不小心写:
58
+
59
+ ``` cpp
60
+ int i;
61
+ cout << i; // 未定义行为!此时 i 还没有初始化
62
+ ```
63
+
64
+ 但是如果你用了 ` auto ` ,那么 ` auto i ` 就会直接报错,提醒你没有赋初始值:
65
+
66
+ ``` cpp
67
+ auto i; // 编译出错,强制提醒你必须赋初始值!
68
+ cout << i;
69
+ ```
70
+
71
+ 你意识到自己漏写了 ` = 0 ` !于是你写上了初始值,编译才能通过。
72
+
73
+ ``` cpp
74
+ auto i = 0 ;
75
+ cout << i;
76
+ ```
77
+
78
+ 可见,只要你养成“总是 ` auto ` ”的好习惯,就绝对不会忘记变量未初始化,因为 ` auto ` 会强制要求有初始值。
79
+
80
+ #### 自动适配类型,避免类型隐式转换
81
+
82
+ 假设你有一个能返回 ` int ` 的函数:
83
+
84
+ ``` cpp
85
+ int getNum ();
86
+ ```
87
+
88
+ 有多处使用了这个函数:
89
+
90
+ ``` cpp
91
+ int a = getNum();
92
+ ...
93
+ int b = getNum() + 1 ;
94
+ ...
95
+ ```
96
+
97
+ 假如你哪天遇到牢板需求改变,它说现在我们的 ` Num ` 需要是浮点数了!
98
+
99
+ ``` cpp
100
+ float getNum ();
101
+ ```
102
+
103
+ 哎呀,你需要把之前那些“多处使用”里写的 ` int ` 全部一个个改成 ` float ` !
104
+
105
+ ``` cpp
106
+ float a = getNum();
107
+ ...
108
+ float b = getNum() + 1 ;
109
+ ...
110
+ ```
111
+
112
+ 如果漏改一个的话,就会发生隐式转换,并且只是警告,不会报错,你根本注意不到,精度就丢失了!
113
+
114
+ 现在“马后炮”一下,如果当时你的“多处使用”用的是 ` auto ` ,那该多好!自动适应!
115
+
116
+ ``` cpp
117
+ auto a = getNum();
118
+ ...
119
+ auto b = getNum() + 1 ;
120
+ ...
121
+ ```
122
+
123
+ 无论你今天 ` getNum ` 想返回 ` float ` 还是 ` double ` ,只需要修改 ` getNum ` 的返回值一处,所有调用了 ` getNum ` 的地方都会自动适配!
124
+
125
+ > {{ icon.fun }} 专治张心欣这种小计级扒皮牢板骚动反复跳脚的情况,无需你一个个去狼狈的改来改回,一处修改,处处生效。
126
+
127
+ #### 统一写法,更可读
128
+
129
+ ``` cpp
130
+ std::vector<int > aVeryLongName (5);
131
+ ```
132
+
133
+ ```cpp
134
+ auto aVeryLongName = std::vector<int>(5);
135
+ ```
136
+
137
+ TODO
138
+
139
+ #### 强制写明字面量类型,避免隐式转换
140
+
141
+ 有同学反映,他想要创建一个 ` size_t ` 类型的整数,初始化为 3。
142
+
143
+ ``` cpp
144
+ size_t i = 3 ; // 3 是 int 类型,这里初始化时发生了隐式转换,int 转为了 size_t
145
+ i = 0xffffffffff ; // OK,在 size_t 范围内(64 位编译器)
146
+ ```
147
+
148
+ 如果直接改用 ` auto ` 的话,因为 ` 3 ` 这个字面量是 ` int ` 类型的,所以初始化出来的 ` auto i ` 也会被推导成 ` int i ` !
149
+
150
+ 虽然目前初始只用到了 ` 3 ` ,然而这位同学后面可能会用到 ` size_t ` 范围的更大整数存入,就存不下了。
151
+
152
+ ``` cpp
153
+ auto i = 3 ; // 错误!auto 会推导为 int 了!
154
+ i = 0xffffffffff ; // 超出 int 范围!
155
+ ```
156
+
157
+ 由于 C++ 是静态编译,变量类型一旦确定就无法更改,我们必须在定义时就指定号范围更大的 ` size_t ` 。
158
+
159
+ 为了让 ` auto ` 推导出这位同学想要的 ` size_t ` 类型,我们可以在 ` 3 ` 这个字面量周围显式写出类型转换,将其转换为 ` size_t ` 。
160
+
161
+ > {{ icon.tip }} 显式类型转换总比隐式的要好!
162
+
163
+ ```
164
+ auto i = (size_t)3; // 正确
165
+ ```
166
+
167
+ 这里的类型转换用的是 C 语言的强制类型转换语法 ` (size_t)3 ` ,更好的写法是用括号包裹的 C++ 构造函数风格的强制类型转换语法:
168
+
169
+ ```
170
+ auto i = size_t(3); // 正确
171
+ ```
172
+
173
+ 看起来就和调用了 ` size_t ` 的“构造函数”一样。这也符合我们前面说的统一写法,类型统一和值写在一起,以括号结合,更可读。
174
+
175
+ > {{ 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 ` 编译器会自动推测一个最合适的。
176
+
177
+ 如果需要其他类型的变量,改用 ` short(3) ` ,` uint8_t(3) ` 配合 ` auto ` 不就行了,根本没必要把类型前置。
178
+
179
+ #### 避免语法歧义
180
+
181
+ TODO
182
+
183
+ ### ` auto ` 的小插曲:初始化列表
184
+
9
185
TODO
10
186
11
187
## 返回类型 ` auto `
12
188
13
- C++11 引入的 ` auto ` 关键字可以用作函数的返回类型,但它只是一个“占位”,让我们得以后置返回类型,并没有多大作用,非常残废。
189
+ C++11 引入的 ` auto ` 关键字可以用作函数的返回类型,但它只是一个“占位”,让我们得以后置返回类型,并没有多大作用,所以 C++11 这版的 ` auto ` 非常残废。
14
190
15
191
``` cpp
16
192
auto f () -> int;
@@ -22,7 +198,7 @@ int f();
22
198
23
199
> {{ icon.detail }} 当初引入后置返回类型实际的用途是 ` auto f(int x) -> decltype(x * x) { return x * x; } ` 这种情况,但很容易被接下来 C++14 引入的真正 ` auto ` 返回类型推导平替了。
24
200
25
- 但是 C++14 引入了函数** 返回类型推导** ,` auto ` 才算真正意义上能用做函数返回类型, 它会自动根据函数中的 ` return ` 表达式推导出函数的返回类型。
201
+ 终于, C++14 引入了函数** 返回类型推导** ,` auto ` 才算真正意义上能用做函数返回类型! 它会自动根据函数中的 ` return ` 表达式推导出函数的返回类型。
26
202
27
203
``` cpp
28
204
auto f (int x) {
0 commit comments