11# 类型推导
22
3- 类型推导是一种能让编译器根据初始化或表达式推断变量类型的机制
3+ 类型推导是一种能让编译器根据初始化或表达式推断变量类型的机制,它能减少代码冗余,提高可维护性。
4+
5+ C++ 提供了多种类型推导机制:
6+
7+ - auto(C++11):根据初始化表达式推导变量类型
8+ - decltype(C++11):根据表达式推导类型,保留完整类型信息
9+ - decltype(auto)(C++14):结合auto和decltype的特性
10+ - 函数返回类型推导(C++14)
11+ - 结构化绑定(C++17)
412
513## 自动类型推导
614
@@ -28,25 +36,51 @@ auto 必须设定一个初始值,因为编译器需要通过初始化表达式
2836auto x; // 编译错误,auto 必须有初始值
2937```
3038
31- auto 在推导时默认会丢弃 const 属性,如果需要保留常量属性,需要显式添加 const。
39+ ### auto 与 const、引用的交互
40+
41+ auto 在推导时默认会丢弃 const 属性和引用属性,如果需要保留,需要显式添加。
3242
3343``` CPP
3444// 常量代码演示
3545const int a{10};
3646
3747auto b{a}; // b 的类型是 int(const 被丢弃)
38- const auto c{a}; // c 的类型是 const int
48+ const auto c{a}; // c 的类型是 const int(显式添加 const)
3949```
4050
51+ 处理引用
52+
4153```CPP
4254int x{10};
4355int& ref = x;
4456
4557auto a{ref}; // a 是 int(引用被丢弃)
46- auto& b{ref}; // b 是 int&
58+ auto& b{ref}; // b 是 int&(显式添加 & 保留引用)
4759```
4860
49- auto 在推导时默认会丢弃引用,如果需要保留引用,必须使用 auto&。
61+ 同时处理 const 和引用
62+
63+ ``` CPP
64+ const int & cref = x;
65+
66+ auto d{cref}; // d 是 int(const 和引用都被丢弃)
67+ const auto& e{cref}; // e 是 const int&(保留 const 和引用)
68+ ```
69+
70+ ### 函数返回类型推导
71+
72+ C++14 起,auto 也可以用于推导函数返回类型
73+
74+ ```CPP
75+ auto add(int a, int b) {
76+ return a + b; // 推导返回类型为 int
77+ }
78+
79+ // 也可以结合 trailing return type
80+ auto multiply(int a, int b) -> int {
81+ return a * b;
82+ }
83+ ```
5084
5185优点:
5286
@@ -62,7 +96,7 @@ auto 在推导时默认会丢弃引用,如果需要保留引用,必须使用
6296
6397## 表达式类型推导
6498
65- 表达式类型推导 ` decltype ` 同样是 C++ 11 引入的关键字,它用于根据表达式本身推导类型,并完整保留类型信息
99+ 表达式类型推导 decltype 同样是 C++ 11 引入的关键字,它用于根据表达式本身推导类型,并完整保留类型信息(包括 const 和引用)
66100
67101使用语法:
68102
@@ -78,12 +112,46 @@ int x{10};
78112decltype(x) y{20}; // y 的类型是 int
79113```
80114
115+ ### decltype 的推导规则
116+
117+ decltype 的推导规则比 auto 更复杂,取决于表达式的形式
118+
119+ 1 . 如果表达式是标识符或类成员访问,则推导为该实体的类型
120+ 2 . 如果表达式是函数调用,则推导为函数返回类型
121+ 3 . 如果表达式是左值表达式(且不是规则 1 的情况),则推导为 T&
122+ 4 . 如果表达式是纯右值表达式,则推导为 T
123+
124+ 代码演示:
125+
126+ ``` CPP
127+ int x = 10 ;
128+ int & ref = x;
129+ const int & cref = x;
130+
131+ // 规则1:标识符
132+ decltype (x) a = 20; // int
133+ decltype(ref) b = x; // int&
134+ decltype(cref) c = x; // const int&
135+
136+ // 规则2:函数调用
137+ int func();
138+ decltype(func()) d = 10; // int
139+
140+ // 规则3:左值表达式(非标识符)
141+ decltype((x)) e = x; // int&(注意双括号!)
142+ decltype(x = 5) f = x; // int&(赋值表达式返回左值)
143+
144+ // 规则4:纯右值表达式
145+ decltype(10) g = 20; // int
146+ decltype(x + 5) h = 20; // int
147+ ```
148+
81149与 auto 不同的是 decltype 会保留 const 属性。
82150
83151```CPP
84152const int a{10};
85153
86- decltype (a) b{10}; // b 的类型是 const int
154+ decltype(a) b{10}; // b 的类型是 const int(完整保留 const)
87155```
88156
89157decltype 会完整保留 const 属性,如果赋值不符合 const 要求将导致编译错误。
@@ -92,14 +160,90 @@ decltype 会完整保留 const 属性,如果赋值不符合 const 要求将导
92160int x{10};
93161
94162decltype (x) a{x}; // int
95- decltype((x)) b{x}; // int&
163+ decltype ((x)) b{x}; // int&(注意双括号,被视为左值表达式)
164+ ```
165+
166+ ## decltype(auto)(C++14)
167+
168+ C++14 引入了 decltype(auto),它结合了 auto 的简洁性和 decltype 的类型保留能力
169+
170+ 代码演示:
171+
172+ ```CPP
173+ int x = 10;
174+ int& ref = x;
175+ const int& cref = x;
176+
177+ decltype(auto) a = x; // int(类似 auto)
178+ decltype(auto) b = ref; // int&(保留引用,类似 decltype)
179+ decltype(auto) c = cref; // const int&(保留 const 和引用)
180+ decltype(auto) d = (x); // int&(注意双括号,类似 decltype((x)))
181+ ```
182+
183+ decltype (auto) 特别适合用于函数返回类型推导,当需要保留引用或 const 属性时
184+
185+ 代码演示:
186+
187+ ``` CPP
188+ // 返回引用
189+ decltype (auto) getRef(int& x) {
190+ return x; // 返回 int&
191+ }
192+
193+ // 返回值
194+ decltype(auto) getValue(int x) {
195+ return x; // 返回 int
196+ }
197+ ```
198+
199+ ## 结构化绑定(C++17)
200+
201+ C++17 引入了结构化绑定,可以同时推导多个变量的类型
202+
203+ 代码演示:
204+
205+ ```CPP
206+ #include <tuple>
207+ #include <map>
208+
209+ std::tuple<int, double, std::string> getPerson() {
210+ return {25, 75.5, "Alice"};
211+ }
212+
213+ int main() {
214+ // 结构化绑定,自动推导类型
215+ auto [age, weight, name] = getPerson();
216+
217+ // 也可以使用 decltype(auto)
218+ decltype(auto) [a, w, n] = getPerson();
219+
220+ // 与 map 一起使用
221+ std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}};
222+ for (const auto& [key, value] : myMap) {
223+ // key 是 int,value 是 std::string
224+ }
225+
226+ return 0;
227+ }
96228```
97229
98- decltype (x) 表示变量类型,而 decltype((x)) 会被视为表达式,从而推导为引用类型。
230+ ## auto 与 decltype 对比
231+
232+ | 特性 | auto | decltype |
233+ | ------| ------| ----------|
234+ | 推导依据 | 初始化表达式 | 任意表达式 |
235+ | const 处理 | 默认丢弃 | 完整保留 |
236+ | 引用处理 | 默认丢弃 | 完整保留 |
237+ | 初始化要求 | 必须初始化 | 不需要(仅推导类型) |
238+ | 适用场景 | 变量声明 | 类型推导、泛型编程 |
239+ | 语法复杂度 | 简单 | 较复杂(有特殊规则) |
99240
100241## 使用建议
101242
102- - 类型复杂时优先使用 auto
103- - 类型明显时可直接使用具体类型
104- - 涉及引用或 const 时使用 decltype
105- - 避免在简单场景中滥用 decltype
243+ - 类型复杂时优先使用 auto:如迭代器、lambda 返回类型、模板类型等
244+ - 类型明显时可直接使用具体类型:如 int、double 等简单类型,提高可读性
245+ - 涉及引用或 const 时使用 decltype:当需要完整保留类型信息时
246+ - 避免在简单场景中滥用 auto:过度使用会降低代码可读性
247+ - C++14 起,考虑使用 decltype (auto):特别是在函数返回类型推导中
248+ - 注意 auto 与列表初始化的交互:避免意外得到 std::initializer_list
249+ - 使用结构化绑定处理多返回值:C++17 起,这是处理元组、pair 等的优雅方式
0 commit comments