Skip to content

Commit abdadc6

Browse files
committed
docs(cpp11/00): overhaul auto/decltype chapter (zh + en)
- add section 二 真实案例 — verbatim std::begin/cbegin cited from the vendored msvc-stl/ basis - restructure to 5 sections; section 三 注意事项 now also covers auto's const/reference stripping - exercises: topic-specific Tips, add char-case asserts to exercise 0, new exercise -5 (const/reference stripping & preservation) - practice section: list all 6 exercises, single entry checker command, collapsible d2x setup, per-chapter discussion link - footer: add mcpp-community org + d2x tool links - fix typos (变得 / 显式) - register exercise -5 in dslings + solutions xmake
1 parent 007856c commit abdadc6

18 files changed

Lines changed: 397 additions & 26 deletions

book/en/src/cpp11/00-auto-and-decltype.md

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ decltype(b) b3; // Can be used without initialization
4444
4545
> Often used for complex expression type deduction to ensure calculation precision
4646
47-
```c++
47+
```cpp
4848
int a = 1;
4949
5050
auto b1 = a + 2;
@@ -58,7 +58,7 @@ decltype(2 + 'a') c2 = 2 + 'a';
5858

5959
**Iterator Type Deduction**
6060

61-
```c++
61+
```cpp
6262
std::vector<int> v = {1, 2, 3};
6363

6464
auto it = v.begin(); // Automatically deduce iterator type
@@ -72,7 +72,7 @@ for (; it != v.end(); ++it) {
7272

7373
> For complex types like functions or lambda expressions, auto and decltype are commonly used. Generally, lambda definitions use auto, while template type parameters use decltype.
7474
75-
```c++
75+
```cpp
7676
int add_func(int a, int b) {
7777
return a + b;
7878
}
@@ -135,7 +135,65 @@ int main() {
135135
}
136136
```
137137

138-
## II. Important Notes - The Impact of Parentheses
138+
## II. Real-World Case - auto/decltype in the STL
139+
140+
> The examples above illustrate syntax; the real value of auto/decltype shows up most directly in the standard library's own implementation. Below we use the in-repo [MSVC STL](https://github.com/mcpp-community/d2mcpp/tree/main/msvc-stl) as the source ([`msvc-stl/stl/inc/xutility`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/xutility#L2200-L2235)); `_EXPORT_STD` / `_NODISCARD` / `_CONSTEXPR17` / `_STD` are internal library macros — ignore them when reading.
141+
142+
### Trailing return type + decltype: std::begin / std::end
143+
144+
`std::begin` / `std::end` (added in C++11) must adapt to any container; their return type depends entirely on `_Cont.begin()` and cannot be written ahead of time, so they "borrow" it via `auto ... -> decltype(...)`
145+
146+
```cpp
147+
// MSVC STL · msvc-stl/stl/inc/xutility (abridged)
148+
_EXPORT_STD template <class _Container>
149+
_NODISCARD _CONSTEXPR17 auto begin(_Container& _Cont) noexcept(noexcept(_Cont.begin())) -> decltype(_Cont.begin()) {
150+
return _Cont.begin();
151+
}
152+
153+
_EXPORT_STD template <class _Container>
154+
_NODISCARD _CONSTEXPR17 auto end(_Container& _Cont) noexcept(noexcept(_Cont.end())) -> decltype(_Cont.end()) {
155+
return _Cont.end();
156+
}
157+
```
158+
159+
This is exactly the trailing-return form from the "Function Return Type Deduction" section, living inside the standard library itself: `auto` as the placeholder + `decltype(_Cont.begin())` precisely deducing the differing iterator types of `vector<int>`, `list<T>`, and so on.
160+
161+
### Reusing another function's return type with decltype: std::cbegin / std::cend
162+
163+
Going further, `std::cbegin` simply reuses `begin`'s return type via `decltype(_STD begin(_Cont))` — it doesn't care what that type is, only that it "matches what begin returns"
164+
165+
```cpp
166+
// MSVC STL · msvc-stl/stl/inc/xutility (abridged)
167+
_EXPORT_STD template <class _Container>
168+
_NODISCARD constexpr auto cbegin(const _Container& _Cont) noexcept(noexcept(_STD begin(_Cont)))
169+
-> decltype(_STD begin(_Cont)) {
170+
return _STD begin(_Cont);
171+
}
172+
```
173+
174+
> Takeaway: when a type "is decided by template parameters and simply cannot be written by hand", the standard library reaches for exactly the auto + decltype toolkit taught in this chapter — one of the core motivations for introducing them in C++11.
175+
176+
## III. Important Notes
177+
178+
### auto and const / reference stripping
179+
180+
> auto deduction **strips top-level const and references**; to keep them you must write `const auto&` / `auto&` explicitly, whereas decltype preserves the declared type exactly
181+
182+
```cpp
183+
const int ci = 1;
184+
int n = 2;
185+
int& ri = n;
186+
187+
auto a = ci; // int — top-level const stripped
188+
auto b = ri; // int — reference stripped (b is an independent copy of n)
189+
190+
const auto& r1 = ci; // const int& — preserved via const auto&
191+
auto&& r2 = ci; // const int& — forwarding reference keeps it
192+
193+
decltype(ci) d = ci; // const int — decltype preserves exactly
194+
```
195+
196+
This is also why `auto a = obj.a;` in "Class/Struct Member Type Deduction" yields `int` rather than `const int` — auto stripped the top-level const.
139197
140198
### Difference between decltype(obj) and decltype( (obj) )
141199
@@ -181,9 +239,51 @@ decltype(b) // Deduction result is declared type int &&
181239
decltype( (b) ) // Deduction result is int &
182240
```
183241

184-
## III. Additional Resources
242+
## IV. Practice Code
243+
244+
### Practice Topics
245+
246+
- 0 - [Declaration & Definition](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp)
247+
- 1 - [Expression Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-1.cpp)
248+
- 2 - [Complex Type Deduction - Iterator / Function](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-2.cpp)
249+
- 3 - [Function Return Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-3.cpp)
250+
- 4 - [Class/Struct Member Type Deduction](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-4.cpp)
251+
- 5 - [const & Reference Stripping and Preservation](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-5.cpp)
252+
253+
### Auto-Checker Commands
254+
255+
<details>
256+
<summary>Don't have d2x yet? Click to expand setup</summary>
257+
258+
```bash
259+
# 1. Install xlings (Linux / macOS)
260+
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash
261+
# Windows PowerShell:
262+
# irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1 | iex
263+
264+
# 2. Install d2x and fetch this tutorial
265+
xlings install d2x -y
266+
d2x install d2mcpp
267+
268+
# 3. Enter the project directory & run the checker
269+
cd d2mcpp
270+
```
271+
272+
</details>
273+
274+
```
275+
d2x checker auto-and-decltype
276+
```
277+
278+
### Exercise Discussion
279+
280+
- [auto/decltype exercise thread](https://forum.d2learn.org/post/357)
281+
282+
## V. Additional Resources
185283

186284
- [Discussion Forum](https://forum.d2learn.org/category/20)
187285
- [d2mcpp Tutorial Repository](https://github.com/mcpp-community/d2mcpp)
286+
- [mcpp-community Organization](https://github.com/mcpp-community)
188287
- [Tutorial Video List](https://space.bilibili.com/65858958/lists/5208246)
189288
- [Tutorial Support Tool - xlings](https://github.com/openxlings/xlings)
289+
- [Exercise Checker - d2x](https://github.com/d2learn/d2x)

book/src/cpp11/00-auto-and-decltype.md

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
# 类型自动推导 - auto和decltype
1010

11-
auto 和 decltype 是C++11引入的强有力的**类型自动推导**工具. 不仅让代码变的更加简洁, 还增强了模板和泛型的表达能力
11+
auto 和 decltype 是C++11引入的强有力的**类型自动推导**工具. 不仅让代码变得更加简洁, 还增强了模板和泛型的表达能力
1212

1313
| Book | Video | Code | X |
1414
| --- | --- | --- | --- |
@@ -23,15 +23,15 @@ auto 和 decltype 是C++11引入的强有力的**类型自动推导**工具. 不
2323

2424
**auto和decltype有什么区别?**
2525

26-
- auto常常用于变量定义, 推导的类型可能丢失const或引用(可显示指定进行保留auto &)
26+
- auto常常用于变量定义, 推导的类型可能丢失const或引用(可显式指定进行保留auto &)
2727
- decltype获取表达式的**精确类型**
2828
- auto通常无法作为模板类型参数使用
2929

3030
## 一、基础用法和场景
3131

3232
### 声明定义
3333

34-
> 充当类型站位符, 辅助变量的定义或声明。使用auto时变量必须要初始化, decltype可以不用初始化
34+
> 充当类型占位符, 辅助变量的定义或声明。使用auto时变量必须要初始化, decltype可以不用初始化
3535
3636
```cpp
3737
int b = 2;
@@ -44,7 +44,7 @@ decltype(b) b3; // 可以不用初始化
4444
4545
> 常常用于复杂表达式的类型推导, 确保计算精度
4646
47-
```c++
47+
```cpp
4848
int a = 1;
4949
5050
auto b1 = a + 2;
@@ -58,7 +58,7 @@ decltype(2 + 'a') c2 = 2 + 'a';
5858

5959
**迭代器类型推导**
6060

61-
```c++
61+
```cpp
6262
std::vector<int> v = {1, 2, 3};
6363

6464
auto it = v.begin(); // 自动推导it类型
@@ -72,7 +72,7 @@ for (; it != v.end(); ++it) {
7272

7373
> 对于函数或lambda表达式这种复杂类型, 常常使用auto和decltype. 一般, lambda定义用auto, 模板类型参数用decltype
7474
75-
```c++
75+
```cpp
7676
int add_func(int a, int b) {
7777
return a + b;
7878
}
@@ -135,11 +135,69 @@ int main() {
135135
}
136136
```
137137

138-
## 二、注意事项 - 括号带来的影响
138+
## 二、真实案例 - STL 中的 auto/decltype
139+
140+
> 前面的例子是为了讲语法, 而 auto/decltype 真正的价值, 在标准库自己的实现里体现得最直接。下面以仓库内置的 [MSVC STL](https://github.com/mcpp-community/d2mcpp/tree/main/msvc-stl) 为例 (源码: [`msvc-stl/stl/inc/xutility`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/xutility#L2200-L2235)); `_EXPORT_STD` / `_NODISCARD` / `_CONSTEXPR17` / `_STD` 是库内部宏, 阅读时可忽略
141+
142+
### 后置返回类型 + decltype:std::begin / std::end
143+
144+
`std::begin` / `std::end` (C++11 新增) 要适配任意容器, 返回类型完全取决于 `_Cont.begin()`, 没法提前写死, 于是直接用 `auto ... -> decltype(...)` 把返回类型"借"过来
145+
146+
```cpp
147+
// MSVC STL · msvc-stl/stl/inc/xutility (有删节)
148+
_EXPORT_STD template <class _Container>
149+
_NODISCARD _CONSTEXPR17 auto begin(_Container& _Cont) noexcept(noexcept(_Cont.begin())) -> decltype(_Cont.begin()) {
150+
return _Cont.begin();
151+
}
152+
153+
_EXPORT_STD template <class _Container>
154+
_NODISCARD _CONSTEXPR17 auto end(_Container& _Cont) noexcept(noexcept(_Cont.end())) -> decltype(_Cont.end()) {
155+
return _Cont.end();
156+
}
157+
```
158+
159+
这正是本章「函数返回值类型推导」一节讲的后置返回写法在标准库里的真身: `auto` 占位 + `decltype(_Cont.begin())` 精确推导出 `vector<int>::iterator`、`list<T>::iterator` 等各不相同的迭代器类型
160+
161+
### 用 decltype 复用另一个函数的返回类型:std::cbegin / std::cend
162+
163+
更进一步, `std::cbegin` 干脆用 `decltype(_STD begin(_Cont))` 直接复用了 `begin` 的返回类型 —— 不必关心它到底是什么, 只要"和 begin 返回的一样"就行
164+
165+
```cpp
166+
// MSVC STL · msvc-stl/stl/inc/xutility (有删节)
167+
_EXPORT_STD template <class _Container>
168+
_NODISCARD constexpr auto cbegin(const _Container& _Cont) noexcept(noexcept(_STD begin(_Cont)))
169+
-> decltype(_STD begin(_Cont)) {
170+
return _STD begin(_Cont);
171+
}
172+
```
173+
174+
> 小结: 面对"类型由模板参数决定、人手根本写不出来"的场景, 标准库用的正是本章这套 auto + decltype 工具。这也是 C++11 当初引入它们的核心动机之一
175+
176+
## 三、注意事项
177+
178+
### auto 的 const / 引用剥离
179+
180+
> auto 推导会**剥离顶层 const 和引用**, 想保留得显式写 `const auto&` / `auto&`; decltype 则精确保留声明类型
181+
182+
```cpp
183+
const int ci = 1;
184+
int n = 2;
185+
int& ri = n;
186+
187+
auto a = ci; // int —— 顶层 const 被剥离
188+
auto b = ri; // int —— 引用被剥离, b 是 n 的独立副本
189+
190+
const auto& r1 = ci; // const int& —— 用 const auto& 保留
191+
auto&& r2 = ci; // const int& —— 万能引用按需保留
192+
193+
decltype(ci) d = ci; // const int —— decltype 精确保留
194+
```
195+
196+
这也解释了前面「类/结构体成员类型推导」里 `auto a = obj.a;` 为什么得到的是 `int` 而非 `const int` —— 顶层 const 被 auto 剥离了
139197
140198
### decltype(obj) 和 decltype( (obj) )的区别
141199
142-
- 一般`decltype(obj)`获取的时其声明类型
200+
- 一般`decltype(obj)`获取的是其声明类型
143201
- 而`decltype( (obj) )` 获取的是 `(obj)` 这个表达式的类型(左值表达式)
144202
145203
```cpp
@@ -181,9 +239,51 @@ decltype(b) // 推导结果是声明类型 int &&
181239
decltype( (b) ) // 推导结果是 int &
182240
```
183241

184-
## 三、其他
242+
## 四、练习代码
243+
244+
### 练习代码主题
245+
246+
- 0 - [声明定义](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp)
247+
- 1 - [表达式类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-1.cpp)
248+
- 2 - [复杂类型推导 - 迭代器 / 函数](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-2.cpp)
249+
- 3 - [函数返回值类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-3.cpp)
250+
- 4 - [类/结构体成员类型推导](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-4.cpp)
251+
- 5 - [const 与引用的剥离和保留](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-5.cpp)
252+
253+
### 练习代码自动检测命令
254+
255+
<details>
256+
<summary>还没有 d2x?点击展开获取方式</summary>
257+
258+
```bash
259+
# 1. 安装 xlings(Linux / macOS)
260+
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash
261+
# Windows PowerShell:
262+
# irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1 | iex
263+
264+
# 2. 安装 d2x 并拉取本教程
265+
xlings install d2x -y
266+
d2x install d2mcpp
267+
268+
# 3. 进入项目目录 & 运行检查命令
269+
cd d2mcpp
270+
```
271+
272+
</details>
273+
274+
```
275+
d2x checker auto-and-decltype
276+
```
277+
278+
### 练习交流讨论
279+
280+
- [auto/decltype 练习讨论帖](https://forum.d2learn.org/post/357)
281+
282+
## 五、其他
185283

186284
- [交流讨论](https://forum.d2learn.org/category/20)
187285
- [d2mcpp教程仓库](https://github.com/mcpp-community/d2mcpp)
286+
- [mcpp-community 社区](https://github.com/mcpp-community)
188287
- [教程视频列表](https://space.bilibili.com/65858958/lists/5208246)
189-
- [教程支持工具-xlings](https://github.com/openxlings/xlings)
288+
- [教程支持工具-xlings](https://github.com/openxlings/xlings)
289+
- [教程检测工具-d2x](https://github.com/d2learn/d2x)

dslings/cpp11/00-auto-and-decltype-0.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Exercise/练习: cpp11 | 00 - auto and decltype | 自动类型推导
66
//
7-
// Tips/提示: 使用 auto decltype 修复代码中的错误
7+
// Tips/提示: auto 从初始值推导类型, 用 decltype 取已声明变量的类型
88
//
99
// Docs/文档:
1010
// - https://en.cppreference.com/w/cpp/language/auto
@@ -39,6 +39,8 @@ int main() {
3939
d2x_assert_eq(a1, a2);
4040
d2x_assert_eq(b, b1);
4141
d2x_assert_eq(b1, b2);
42+
d2x_assert_eq(c, c1);
43+
d2x_assert_eq(c1, c2);
4244

4345
D2X_WAIT
4446

dslings/cpp11/00-auto-and-decltype-1.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Exercise/练习: cpp11 | 00 - auto and decltype | 表达式类型推导
66
//
7-
// Tips/提示: 使用 auto 和 decltype 修复代码中的错误
7+
// Tips/提示: 含浮点的表达式要推导成 double, 别用 int 截断了精度
88
//
99
// Docs/文档:
1010
// - https://en.cppreference.com/w/cpp/language/auto

dslings/cpp11/00-auto-and-decltype-2.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Exercise/练习: cpp11 | 00 - auto and decltype | 复杂类型推导
66
//
7-
// Tips/提示: 使用 autodecltype 修复代码中的错误
7+
// Tips/提示: 迭代器用 auto, std::function 的模板参数用 decltype(函数名), 替代手写长类型
88
//
99
// Docs/文档:
1010
// - https://en.cppreference.com/w/cpp/language/auto

dslings/cpp11/00-auto-and-decltype-3.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Exercise/练习: cpp11 | 00 - auto and decltype | 函数返回值类型推导
66
//
7-
// Tips/提示: 使用 auto decltype 修复代码中的错误
7+
// Tips/提示: 用后置返回 auto ... -> decltype(a - b) 让模板函数自动推导返回类型
88
//
99
// Docs/文档:
1010
// - https://en.cppreference.com/w/cpp/language/auto

dslings/cpp11/00-auto-and-decltype-4.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Exercise/练习: cpp11 | 00 - auto and decltype | 类/结构体成员类型推导
66
//
7-
// Tips/提示: 在D2X_YOUR_ANSWER处填写正确的推导类型
7+
// Tips/提示: 区分 decltype(obj) 与 decltype((obj)), 带括号是表达式会推导出引用类型
88
//
99
// Docs/文档:
1010
// - https://en.cppreference.com/w/cpp/language/auto

0 commit comments

Comments
 (0)