Skip to content

Commit 79ffbe6

Browse files
authored
video(cpp11/00): manim animation for auto/decltype (#53)
* video(cpp11/00): add manim animation for auto/decltype Manim Community v0.18.1 scene mirroring the existing videos/cpp11/* framework (MovingCameraScene + d2x mcpp_video_start/end, DHighlight, create_code_helper). Six scenes following the chapter: declaration, expression deduction, complex types (iterator), trailing-return + decltype, const/reference stripping, and the decltype parentheses pitfall. Run: uv run --with "manim==0.18.1" manim -pql videos/cpp11/00-auto-and-decltype.py * docs(cpp11/00): sync bilingual docs + add animation video link - Sync en doc to zh: iterator example (v.insert) and const/reference stripping example now match the zh version's code - Add Bilibili animation link (BV1EzJs6HEf7) alongside the existing explanation video in both book docs and videos/README.md - Add render.sh launcher and refine the manim animation script * docs(cpp11/00): correct video labels — 视频解读=BV1EzJs6HEf7, 练习讲解=BV1xkdYYUEyH
1 parent 173f145 commit 79ffbe6

6 files changed

Lines changed: 640 additions & 72 deletions

File tree

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

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ auto and decltype are powerful **type deduction** tools introduced in C++11. The
1212

1313
| Book | Video | Code | X |
1414
| --- | --- | --- | --- |
15-
| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/00-auto-and-decltype.md) | [Video Explanation](https://www.bilibili.com/video/BV1xkdYYUEyH) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp) | |
15+
| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/00-auto-and-decltype.md) | [Video Explanation](https://www.bilibili.com/video/BV1EzJs6HEf7) / [Exercise Walkthrough](https://www.bilibili.com/video/BV1xkdYYUEyH) | [Practice Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp) | |
1616

1717

1818
**Why were they introduced?**
@@ -61,10 +61,14 @@ decltype(2 + 'a') c2 = 2 + 'a';
6161
```cpp
6262
std::vector<int> v = {1, 2, 3};
6363

64+
// std::vector<int>::iterator it = v.begin();
6465
auto it = v.begin(); // Automatically deduce iterator type
6566
// decltype(v.begin()) it = v.begin();
6667
for (; it != v.end(); ++it) {
67-
std::cout << *it << " ";
68+
if (*it == 2) {
69+
v.insert(it, 0);
70+
break;
71+
}
6872
}
6973
```
7074

@@ -137,41 +141,42 @@ int main() {
137141

138142
## II. Real-World Case - auto/decltype in the STL
139143

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.
144+
> The examples above illustrate syntax; the practical value of auto/decltype is demonstrated most directly in the standard library's own implementation. The following picks two common pieces of code from the in-repo [MSVC STL](https://github.com/mcpp-community/d2mcpp/tree/main/msvc-stl) to demonstrate `auto` and `decltype` respectively; `_STD` and the like are internal library macros and qualifiers — focus on the `auto` / `decltype` when reading.
141145
142-
### Trailing return type + decltype: std::begin / std::end
146+
### auto deduces iterator and element types: traversing a container
143147

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(...)`
148+
Traversing a container is the most common scenario for `auto`. Below is a traversal from path normalization in `<filesystem>`: `auto _Pos` deduces the iterator type, and `const auto _Elem` deduces the element type obtained by dereferencing — the very same form as `auto it = v.begin()` from "Complex type deduction - iterators" in `## I`.
145149

146150
```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-
}
151+
// MSVC STL · msvc-stl/stl/inc/filesystem (abridged, original indentation kept)
152+
auto _New_end = _Vec.begin();
153+
for (auto _Pos = _Vec.begin(); _Pos != _Vec.end();) {
154+
const auto _Elem = *_Pos++;
155+
// ...(decide whether to write _Elem back to _New_end; omitted)
156+
}
157157
```
158158
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.
159+
`_Vec` is the container of path components; both its iterator type and its element type are left to `auto`, with no need to spell out the concrete types.
160160
161-
### Reusing another function's return type with decltype: std::cbegin / std::cend
161+
### decltype takes the type of a variable: the binary search in std::lower_bound
162162
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"
163+
The most direct use of `decltype` is "take the type of a variable or expression". In the standard library's binary search `std::lower_bound`, `auto` first deduces the range length `_Count`, then `decltype(_Count)` denotes "the same type as `_Count`" to convert `_Count / 2` back to that type:
164164
165165
```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-
}
166+
// MSVC STL · msvc-stl/stl/inc/xutility (abridged) —— std::lower_bound
167+
auto _UFirst = _STD _Get_unwrapped(_First);
168+
auto _Count = _STD distance(_UFirst, _STD _Get_unwrapped(_Last));
169+
170+
while (0 < _Count) { // divide and conquer, find half that contains answer
171+
const auto _Count2 = static_cast<decltype(_Count)>(_Count / 2);
172+
const auto _UMid = _STD next(_UFirst, _Count2);
173+
// ...(compare at _UMid, narrow the range; omitted)
174+
}
172175
```
173176

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.
177+
This is the same use as `decltype(b) b2` from "Declaration and definition" in `## I`: `decltype(_Count)` is simply "the type of `_Count`". `auto` deduces the type, and `decltype` reuses that same type elsewhere.
178+
179+
> Takeaway: traversing a container, reusing the type of some variable — these everyday forms are, inside the standard library, exactly the auto + decltype toolkit taught in this chapter. This is one of the core motivations for introducing them in C++11.
175180
176181
## III. Important Notes
177182

@@ -180,17 +185,21 @@ _NODISCARD constexpr auto cbegin(const _Container& _Cont) noexcept(noexcept(_STD
180185
> 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
181186
182187
```cpp
183-
const int ci = 1;
184-
int n = 2;
185-
int& ri = n;
188+
int a = 1;
189+
int &b = a;
190+
const int c = 1;
191+
const int &d = c;
186192

187-
auto a = ci; // int — top-level const stripped
188-
auto b = ri; // int — reference stripped (b is an independent copy of n)
193+
auto a1 = a; // int
194+
auto b1 = b; // int
195+
auto c1 = c; // int
196+
auto d1 = d; // int
189197

190-
const auto& r1 = ci; // const int& — preserved via const auto&
191-
auto&& r2 = ci; // const int& — forwarding reference keeps it
198+
const auto c2 = c; // const int
199+
const auto &d2 = d; // const int &
192200

193-
decltype(ci) d = ci; // const int — decltype preserves exactly
201+
decltype(c) c3 = c; // const int
202+
decltype(d) d3 = d; // const int &
194203
```
195204
196205
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.

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

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ auto 和 decltype 是C++11引入的强有力的**类型自动推导**工具. 不
1212

1313
| Book | Video | Code | X |
1414
| --- | --- | --- | --- |
15-
| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/00-auto-and-decltype.md) | [视频解读](https://www.bilibili.com/video/BV1xkdYYUEyH) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp) | |
15+
| [cppreference-auto](https://en.cppreference.com/w/cpp/language/auto) / [cppreference-decltype](https://en.cppreference.com/w/cpp/language/decltype) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/00-auto-and-decltype.md) | [视频解读](https://www.bilibili.com/video/BV1EzJs6HEf7) / [练习讲解](https://www.bilibili.com/video/BV1xkdYYUEyH) | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/00-auto-and-decltype-0.cpp) | |
1616

1717

1818
**为什么引入?**
@@ -61,10 +61,14 @@ decltype(2 + 'a') c2 = 2 + 'a';
6161
```cpp
6262
std::vector<int> v = {1, 2, 3};
6363

64+
// std::vector<int>::iterator it = v.begin();
6465
auto it = v.begin(); // 自动推导it类型
6566
// decltype(v.begin()) it = v.begin();
6667
for (; it != v.end(); ++it) {
67-
std::cout << *it << " ";
68+
if (*it == 2) {
69+
v.insert(it, 0);
70+
break;
71+
}
6872
}
6973
```
7074

@@ -137,41 +141,42 @@ int main() {
137141

138142
## 二、真实案例 - STL 中的 auto/decltype
139143

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` 是库内部宏, 阅读时可忽略
144+
> 前述示例用于讲解语法, 而 auto/decltype 的实际价值, 在标准库自身的实现中体现得最为直接。下面从仓库内置的 [MSVC STL](https://github.com/mcpp-community/d2mcpp/tree/main/msvc-stl) 中选取两段常见代码, 分别演示 `auto``decltype`; `_STD` 等是库内部的宏与限定写法, 阅读时关注 `auto` / `decltype` 即可
141145
142-
### 后置返回类型 + decltype:std::begin / std::end
146+
### auto 推导迭代器与元素类型:遍历容器
143147

144-
`std::begin` / `std::end` (C++11 新增) 要适配任意容器, 返回类型完全取决于 `_Cont.begin()`, 没法提前写死, 于是直接用 `auto ... -> decltype(...)` 把返回类型"借"过来
148+
遍历容器是 `auto` 最常见的场景。下面是 `<filesystem>` 规范化路径时的一段遍历:`auto _Pos` 推导出迭代器类型, `const auto _Elem` 推导出解引用后的元素类型 —— 与 `## 一`「复杂类型推导 - 迭代器」里的 `auto it = v.begin()` 完全是同一种写法
145149

146150
```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-
}
151+
// MSVC STL · msvc-stl/stl/inc/filesystem (有删节, 缩进保留源码层级)
152+
auto _New_end = _Vec.begin();
153+
for (auto _Pos = _Vec.begin(); _Pos != _Vec.end();) {
154+
const auto _Elem = *_Pos++;
155+
// ...(根据 _Elem 决定是否写回 _New_end, 略)
156+
}
157157
```
158158
159-
这正是本章「函数返回值类型推导」一节讲的后置返回写法在标准库里的真身: `auto` 占位 + `decltype(_Cont.begin())` 精确推导出 `vector<int>::iterator`、`list<T>::iterator` 等各不相同的迭代器类型
159+
`_Vec` 是路径分量的容器, 它的迭代器类型与元素类型都交给 `auto` 推导, 无需写出具体类型
160160
161-
### decltype 复用另一个函数的返回类型:std::cbegin / std::cend
161+
### decltype 取变量的类型:std::lower_bound 的二分查找
162162
163-
更进一步, `std::cbegin` 干脆用 `decltype(_STD begin(_Cont))` 直接复用了 `begin` 的返回类型 —— 不必关心它到底是什么, 只要"和 begin 返回的一样"就行
163+
`decltype` 最直接的用法就是"取某个变量或表达式的类型"。标准库的二分查找 `std::lower_bound` 里, 先用 `auto` 推导出区间长度 `_Count`, 再用 `decltype(_Count)` 表示"与 `_Count` 相同的类型", 把 `_Count / 2` 转换回该类型:
164164
165165
```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-
}
166+
// MSVC STL · msvc-stl/stl/inc/xutility (有删节) —— std::lower_bound
167+
auto _UFirst = _STD _Get_unwrapped(_First);
168+
auto _Count = _STD distance(_UFirst, _STD _Get_unwrapped(_Last));
169+
170+
while (0 < _Count) { // divide and conquer, find half that contains answer
171+
const auto _Count2 = static_cast<decltype(_Count)>(_Count / 2);
172+
const auto _UMid = _STD next(_UFirst, _Count2);
173+
// ...(在 _UMid 处比较, 缩小区间, 略)
174+
}
172175
```
173176

174-
> 小结: 面对"类型由模板参数决定、人手根本写不出来"的场景, 标准库用的正是本章这套 auto + decltype 工具。这也是 C++11 当初引入它们的核心动机之一
177+
这正是 `## 一`「声明定义」中 `decltype(b) b2` 的同款用法: `decltype(_Count)` 就是"`_Count` 的类型"。`auto` 负责把类型推导出来, `decltype` 负责在别处复用同一个类型
178+
179+
> 小结: 遍历容器、复用某个变量的类型 —— 这些日常写法在标准库内部用的正是本章这套 auto + decltype 工具。这也是 C++11 引入二者的核心动机之一
175180
176181
## 三、注意事项
177182

@@ -180,17 +185,21 @@ _NODISCARD constexpr auto cbegin(const _Container& _Cont) noexcept(noexcept(_STD
180185
> auto 推导会**剥离顶层 const 和引用**, 想保留得显式写 `const auto&` / `auto&`; decltype 则精确保留声明类型
181186
182187
```cpp
183-
const int ci = 1;
184-
int n = 2;
185-
int& ri = n;
188+
int a = 1;
189+
int &b = a;
190+
const int c = 1;
191+
const int &d = c;
186192

187-
auto a = ci; // int —— 顶层 const 被剥离
188-
auto b = ri; // int —— 引用被剥离, b 是 n 的独立副本
193+
auto a1 = a; // int
194+
auto b1 = b; // int
195+
auto c1 = c; // int
196+
auto d1 = d; // int
189197

190-
const auto& r1 = ci; // const int& —— 用 const auto& 保留
191-
auto&& r2 = ci; // const int& —— 万能引用按需保留
198+
const auto c2 = c; // const int
199+
const auto &d2 = d; // const int &
192200

193-
decltype(ci) d = ci; // const int —— decltype 精确保留
201+
decltype(c) c3 = c; // const int
202+
decltype(d) d3 = d; // const int &
194203
```
195204
196205
这也解释了前面「类/结构体成员类型推导」里 `auto a = obj.a;` 为什么得到的是 `int` 而非 `const int` —— 顶层 const 被 auto 剥离了

videos/README.md

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,47 @@
22

33
## 动画代码
44

5-
```python
6-
# manim -pql videos/[cppxx]/[filename].py
7-
manim -pql videos/cpp11/09-list-initialization.py
5+
> Manim Community v0.18.1。manim 本体由 uv 装(纯 pip);pycairo/manimpango 的原生依赖
6+
> —— xcb-free 的 cairo/pango 图形栈 —— 用 xlings 装进项目 subos,免去系统 gcc 的 xcb 坑。
7+
8+
```bash
9+
# 1. 一次性:装图形栈 + uv 进项目 subos(cairo/pango 的 deps 自动拉全树)
10+
xlings install cairo pango uv
11+
12+
# 2. 渲染:用启动器(已封装 PKG_CONFIG_PATH / uv 依赖 / pygments pin,免手输环境变量)
13+
videos/render.sh cpp11/00-auto-and-decltype.py # 默认 -ql 快预览
14+
videos/render.sh cpp11/00-auto-and-decltype.py -qh # 高清 1080p
15+
```
16+
17+
<details>
18+
<summary>启动器封装了什么 / 手动等价命令</summary>
19+
20+
`videos/render.sh <场景> [manim参数]` 等价于(在仓库根执行):
21+
22+
```bash
23+
SR=$PWD/.xlings/subos/_/usr # 项目匿名 subos —— xlings install 装在这, xlings-gcc 默认也搜它
24+
PKG_CONFIG_PATH=$SR/lib/pkgconfig \
25+
uv run --with "manim==0.18.1" --with "pygments==2.17.2" \
26+
manim -ql videos/cpp11/00-auto-and-decltype.py
827
```
928

10-
> Note: Manim Community v0.18.1
29+
- `PKG_CONFIG_PATH` 指向项目 subos,让 pkg-config 命中 **xcb-free cairo**(而非宿主 apt 带 xcb 的)——仅首次/清缓存后编译 pycairo/manimpango 时需要,编好的 wheel 被 uv 缓存后可省。
30+
- `pygments==2.17.2` 避开 manim 0.18.1 的 `Code` 渲染 `#CCC` bug。
31+
- **兜底**(没装生态栈时,用系统 gcc 绕开 xlings-gcc 的 xcb 坑):
32+
```bash
33+
CC=/usr/bin/gcc CXX=/usr/bin/g++ \
34+
uv run --with "manim==0.18.1" --with "pygments==2.17.2" \
35+
manim -ql videos/cpp11/00-auto-and-decltype.py
36+
```
37+
</details>
1138

1239
## 视频列表
1340

1441
| c++标准 | 特性 | 标题 | 练习代码/视频 | 备注 |
1542
| --- | --- | --- | --- | --- |
1643
| **引导** | `项目使用教程/引导` | hello mcpp | [docs](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/base/chapter_1.md) / [code](/dslings/hello-mcpp.cpp) / [video](https://www.bilibili.com/video/BV182MtzPEiX?p=2) | |
17-
| **cpp11** | `00 - auto和decltype` | 类型自动推导 | [docs](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/00-auto-and-decltype.md) / [code](/dslings/cpp11/00-auto-and-decltype-0.cpp) / [video](https://www.bilibili.com/video/BV1xkdYYUEyH) | |
44+
| **cpp11** | `00 - auto和decltype` | 类型自动推导 | [docs](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/00-auto-and-decltype.md) / [code](/videos/cpp11/00-auto-and-decltype.py) / [video](https://www.bilibili.com/video/BV1EzJs6HEf7) | |
45+
| | | 练习讲解 | [code](/dslings/cpp11/00-auto-and-decltype-0.cpp) / [video](https://www.bilibili.com/video/BV1xkdYYUEyH) | |
1846
| | | decltype注意事项 | [code](/dslings/cpp11/00-auto-and-decltype-4.cpp) / [video](https://www.bilibili.com/video/BV1KWoMYUEzW) | [补充](https://forum.d2learn.org/topic/82) |
1947
| | `01 - default和delete` | 控制默认构造函数生成 | [code](/dslings/cpp11/01-default-and-delete-0.cpp) / [video](https://www.bilibili.com/video/BV1B35pz5EN2) | |
2048
| | | 类型对象行为控制示例 | [code](/dslings/cpp11/01-default-and-delete-1.cpp) / [video](https://www.bilibili.com/video/BV1Vg5tznE8o) | |

0 commit comments

Comments
 (0)