Skip to content

Commit 6cf6ff2

Browse files
Chapter11 1,2
1 parent 8b9dbdc commit 6cf6ff2

18 files changed

Lines changed: 3715 additions & 10 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
project(chapter11_auto_decltype CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
set(CMAKE_CXX_EXTENSIONS OFF)
7+
8+
# 嵌入式友好选项
9+
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
10+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
11+
12+
# 为每个示例创建可执行文件
13+
add_executable(basic_auto basic_auto.cpp)
14+
add_executable(auto_with_references auto_with_references.cpp)
15+
add_executable(decltype_basics decltype_basics.cpp)
16+
add_executable(decltype_auto_trailing_return decltype_auto_trailing_return.cpp)
17+
add_executable(embedded_hal_types embedded_hal_types.cpp)
18+
add_executable(auto_pitfalls auto_pitfalls.cpp)
19+
20+
# 可选:禁用异常和RTTI(嵌入式常见)
21+
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# auto与decltype 示例代码
2+
3+
本目录包含了Chapter 11.1 "自动类型推导:auto与decltype"的所有示例代码。
4+
5+
## 示例列表
6+
7+
### 1. basic_auto.cpp
8+
**基础auto用法演示**
9+
- 基本类型推导
10+
- 范围for循环中的auto
11+
- 复杂类型简化
12+
- 函数返回值
13+
14+
**编译运行**:
15+
```bash
16+
g++ -std=c++17 basic_auto.cpp -o basic_auto
17+
./basic_auto
18+
```
19+
20+
### 2. auto_with_references.cpp
21+
**auto与引用、const的推导规则**
22+
- 引用推导规则
23+
- const auto& 避免拷贝
24+
- 常见陷阱演示
25+
26+
**编译运行**:
27+
```bash
28+
g++ -std=c++17 auto_with_references.cpp -o auto_with_references
29+
./auto_with_references
30+
```
31+
32+
### 3. decltype_basics.cpp
33+
**decltype关键字基础**
34+
- decltype保留类型精确信息
35+
- 双括号规则 `decltype((x))`
36+
- decltype用于表达式
37+
- 类型别名
38+
39+
**编译运行**:
40+
```bash
41+
g++ -std=c++17 decltype_basics.cpp -o decltype_basics
42+
./decltype_basics
43+
```
44+
45+
### 4. decltype_auto_trailing_return.cpp
46+
**decltype(auto)和尾返回类型**
47+
- C++11尾返回类型语法
48+
- C++14 decltype(auto)
49+
- 完美转发返回值
50+
- 实际应用:配置解析器
51+
52+
**编译运行**:
53+
```bash
54+
g++ -std=c++17 decltype_auto_trailing_return.cpp -o decltype_auto_trailing_return
55+
./decltype_auto_trailing_return
56+
```
57+
58+
### 5. embedded_hal_types.cpp
59+
**嵌入式HAL库类型中的auto应用**
60+
- 简化HAL类型声明
61+
- 寄存器访问辅助类
62+
- DMA配置
63+
- 中断处理程序
64+
65+
**编译运行**:
66+
```bash
67+
g++ -std=c++17 embedded_hal_types.cpp -o embedded_hal_types
68+
./embedded_hal_types
69+
```
70+
71+
### 6. auto_pitfalls.cpp
72+
**auto使用的常见陷阱**
73+
- 意外的拷贝
74+
- 代理类型(vector<bool>)
75+
- 初始化列表推导
76+
- decltype(auto)悬空引用
77+
- std::function vs auto
78+
79+
**编译运行**:
80+
```bash
81+
g++ -std=c++17 auto_pitfalls.cpp -o auto_pitfalls
82+
./auto_pitfalls
83+
```
84+
85+
## 使用CMake构建
86+
87+
```bash
88+
mkdir build
89+
cd build
90+
cmake ..
91+
make
92+
```
93+
94+
## 编译器要求
95+
96+
- C++17或更高版本
97+
- 支持的编译器:GCC 7+, Clang 5+, MSVC 2017+
98+
99+
## 嵌入式编译
100+
101+
对于嵌入式平台,可以添加以下选项:
102+
103+
```bash
104+
g++ -std=c++17 -fno-exceptions -fno-rtti basic_auto.cpp -o basic_auto_embedded
105+
```
106+
107+
或在CMakeLists.txt中取消注释:
108+
```cmake
109+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
110+
```
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// auto_pitfalls.cpp - auto使用的常见陷阱
2+
#include <iostream>
3+
#include <vector>
4+
#include <type_traits>
5+
#include <initializer_list>
6+
#include <array>
7+
#include <functional>
8+
9+
void pitfall_1_unexpected_copy() {
10+
std::cout << "=== 陷阱1:意外的拷贝 ===\n\n";
11+
12+
struct LargeData {
13+
std::array<int, 1000> data{};
14+
int id{};
15+
16+
LargeData() {
17+
data.fill(42);
18+
}
19+
20+
// 拷贝构造函数(带日志)
21+
LargeData(const LargeData& other) : data(other.data), id(other.id) {
22+
std::cout << " [拷贝构造] LargeData拷贝了!\n";
23+
}
24+
25+
// 移动构造函数(带日志)
26+
LargeData(LargeData&& other) noexcept
27+
: data(std::move(other.data)), id(other.id) {
28+
std::cout << " [移动构造] LargeData移动了\n";
29+
}
30+
};
31+
32+
std::vector<LargeData> container;
33+
container.emplace_back();
34+
container[0].id = 1;
35+
36+
std::cout << "1. 使用auto(按值拷贝):\n";
37+
for (auto item : container) {
38+
std::cout << " 处理数据ID: " << item.id << '\n';
39+
}
40+
41+
std::cout << "\n2. 使用const auto&(避免拷贝):\n";
42+
for (const auto& item : container) {
43+
std::cout << " 处理数据ID: " << item.id << '\n';
44+
}
45+
46+
std::cout << "\n3. 使用auto&(需要修改时):\n";
47+
for (auto& item : container) {
48+
item.id *= 10;
49+
}
50+
std::cout << " 修改后第一个元素ID: " << container[0].id << '\n';
51+
}
52+
53+
void pitfall_2_proxy_types() {
54+
std::cout << "\n=== 陷阱2:代理类型(vector<bool>) ===\n\n";
55+
56+
std::vector<bool> flags = {true, false, true, false, true};
57+
58+
std::cout << "1. 尝试使用auto&修改:\n";
59+
// auto& bit = flags[0]; // 编译错误!
60+
std::cout << " auto& 编译错误:vector<bool>::operator[]返回代理类型\n";
61+
62+
std::cout << "\n2. 使用auto(拷贝):\n";
63+
auto bit = flags[0]; // bit是一个新的bool值
64+
std::cout << " bit = " << std::boolalpha << bit << '\n';
65+
bit = false;
66+
std::cout << " 修改bit后,flags[0] = " << flags[0] << "(未改变)\n";
67+
68+
std::cout << "\n3. 正确修改vector<bool>:\n";
69+
flags[0] = false;
70+
std::cout << " 直接赋值后,flags[0] = " << flags[0] << '\n';
71+
}
72+
73+
void pitfall_3_initializer_list() {
74+
std::cout << "\n=== 陷阱3:初始化列表推导 ===\n\n";
75+
76+
std::cout << "1. auto x = {1, 2, 3};\n";
77+
auto x = {1, 2, 3}; // std::initializer_list<int>
78+
std::cout << " 类型: std::initializer_list<int>\n";
79+
std::cout << " 大小: " << x.size() << '\n';
80+
81+
std::cout << "\n2. auto y{1, 2, 3}; (C++17)\n";
82+
// auto y{1, 2, 3}; // C++17+: 错误!多个元素
83+
std::cout << " C++17+中,auto{多个元素}是错误\n";
84+
85+
std::cout << "\n3. auto z{42}; (C++17)\n";
86+
auto z{42}; // C++17+: int(不是initializer_list)
87+
std::cout << " C++17+: 类型是int\n";
88+
std::cout << " 值: " << z << '\n';
89+
90+
// 函数参数问题
91+
std::cout << "\n4. 函数返回initializer_list:\n";
92+
auto get_list = []() -> std::initializer_list<int> {
93+
return {1, 2, 3}; // 返回std::initializer_list<int>
94+
};
95+
auto list = get_list();
96+
std::cout << " 获取的列表大小: " << list.size() << '\n';
97+
98+
// 陷阱:悬空引用
99+
std::cout << "\n5. 初始化列表的生命周期:\n";
100+
// 错误示例(注释掉):
101+
// const auto& danger = std::vector<int>{1, 2, 3}[0]; // 临时对象销毁
102+
// std::cout << danger; // 未定义行为
103+
std::cout << " 注意:绑定到临时对象的引用可能悬空\n";
104+
}
105+
106+
void pitfall_4_function_template_deduction() {
107+
std::cout << "\n=== 陷阱4:函数模板推导冲突 ===\n\n";
108+
109+
std::vector<int> v = {1, 2, 3};
110+
111+
std::cout << "1. auto vs 函数参数推导:\n";
112+
auto x = v[0]; // int(拷贝)
113+
const auto& y = v[0]; // const int&(引用)
114+
115+
std::cout << " auto: int\n";
116+
std::cout << " const auto&: const int&\n";
117+
118+
// 模板函数
119+
auto process = [](auto t) {
120+
std::cout << " 类型: " << typeid(t).name() << '\n';
121+
return t;
122+
};
123+
124+
std::cout << "\n2. 模板函数推导:\n";
125+
process(v[0]); // T推导为int
126+
127+
std::cout << "\n3. 显式指定类型:\n";
128+
// 注意:泛型lambda不能直接显式指定模板参数
129+
// 需要用其他方式,比如包装成std::function或使用模板函数
130+
auto const_ref_process = [](const auto& t) {
131+
std::cout << " 类型: " << typeid(t).name() << '\n';
132+
return t;
133+
};
134+
const_ref_process(v[0]); // 强制为const int&
135+
}
136+
137+
void pitfall_5_decltype_auto_dangling() {
138+
std::cout << "\n=== 陷阱5:decltype(auto)悬空引用 ===\n\n";
139+
140+
std::cout << "1. 危险:返回局部变量的引用\n";
141+
142+
// 错误示例(编译时可能被捕获,也可能不被捕获)
143+
/*
144+
auto dangerous = []() -> decltype(auto) {
145+
int x = 42;
146+
return (x); // 返回局部变量的引用!未定义行为
147+
};
148+
*/
149+
150+
std::cout << " 返回 (x) 而不是 x 会创建引用\n";
151+
152+
std::cout << "\n2. 正确做法:\n";
153+
auto safe_by_value = []() -> int {
154+
int x = 42;
155+
return x; // 返回int
156+
};
157+
158+
auto safe_by_static_ref = []() -> decltype(auto) {
159+
static int x = 42;
160+
return (x); // OK:静态变量
161+
};
162+
163+
std::cout << " safe_by_value: " << safe_by_value() << '\n';
164+
std::cout << " safe_by_static_ref: " << safe_by_static_ref() << '\n';
165+
166+
std::cout << "\n3. decltype(auto)的括号陷阱:\n";
167+
int value = 100;
168+
decltype(auto) ref1 = value; // int
169+
decltype(auto) ref2 = (value); // int&
170+
171+
std::cout << " decltype(auto) ref1 = value: int\n";
172+
std::cout << " decltype(auto) ref2 = (value): int&\n";
173+
}
174+
175+
void pitfall_6_std_function_vs_auto() {
176+
std::cout << "\n=== 陷阱6:std::function vs auto ===\n\n";
177+
178+
auto lambda = [](int x) { return x * 2; };
179+
180+
std::cout << "1. 类型差异:\n";
181+
auto lambda_copy = lambda; // lambda的具体类型(未知类型名)
182+
std::function<int(int)> func = lambda; // std::function包装
183+
184+
std::cout << " sizeof(lambda): " << sizeof(lambda) << " 字节\n";
185+
std::cout << " sizeof(std::function): " << sizeof(func) << " 字节\n";
186+
std::cout << " std::function有额外开销!\n";
187+
188+
std::cout << "\n2. 性能考虑:\n";
189+
std::cout << " auto: 零开销,直接内联\n";
190+
std::cout << " std::function: 虚函数调用开销\n";
191+
192+
std::cout << "\n3. 存储需求:\n";
193+
std::cout << " auto: 必须在同一作用域内使用\n";
194+
std::cout << " std::function: 可以存储在容器中\n";
195+
}
196+
197+
int main() {
198+
pitfall_1_unexpected_copy();
199+
pitfall_2_proxy_types();
200+
pitfall_3_initializer_list();
201+
pitfall_4_function_template_deduction();
202+
pitfall_5_decltype_auto_dangling();
203+
pitfall_6_std_function_vs_auto();
204+
205+
std::cout << "\n=== 总结 ===\n";
206+
std::cout << "\n使用auto的最佳实践:\n";
207+
std::cout << "1. 范围for循环优先用 const auto&\n";
208+
std::cout << "2. 需要修改时用 auto&\n";
209+
std::cout << "3. 注意初始化列表的特殊推导\n";
210+
std::cout << "4. 小心decltype(auto)的括号规则\n";
211+
std::cout << "5. 优先用auto而不是std::function(除非需要存储)\n";
212+
213+
return 0;
214+
}

0 commit comments

Comments
 (0)