Skip to content

Commit 173f145

Browse files
lczllxCopilot
andauthored
docs(cpp11): add STL real-world case, exercise mapping, and tagged-union exercise for 16-generalized-unions (#57)
* docs(cpp11): add STL real-world case and exercise mapping for 16-generalized-unions - add section 二 真实案例 — include std::variant _Variant_storage_ and std::any _Storage_t examples, both cited from vendored msvc-stl/ - fill in 四 练习代码 — exercise descriptions, file links and d2x checker command - fix empty Code link in header table Case selection notes: Picked _Variant_storage_ over std::optional _Optional_destruct_base because the variant recursive union demonstrates both key C++11 generalized-union capabilities — non-trivial members and manual destructor management — while the optional pattern is effectively a special case of variant. std::any adds a second typical use: union as type-erased storage. Both are directly traceable in the vendored msvc-stl/, consistent with how ch00 cites xutility. * docs(cpp11): fix exercise list format to match project convention * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * docs(cpp11): apply Copilot review suggestions — remove inaccurate noexcept, fix 类型安全 wording * feat(cpp11): add tagged-union exercise for 16-generalized-unions Add exercise 2 — tagged/discriminated union combining enum tag + union to implement a simplified std::variant-like Value type. Exercise progression: 0. union default member initialization (static rule) 1. non-trivial type + placement new + manual destructor 2. enum tag + union → type-safe discriminated union Design rationale: ex 2 ties ex 0 and ex 1 together by adding a tag enum to track the active member — the exact pattern used by std::variant's internal _Variant_storage_, directly echoing the STL real-world case in the chapter. Learners construct/destruct/switch members based on tag, covering all three C++11 generalized-union capabilities in one practical exercise. * docs(cpp11): sync en book chapter with zh for 16-generalized-unions Apply the same updates to the English book chapter: - add section II Real-World Case (msvc-stl variant + any examples) - fill in section IV Exercise Code (exercise links + d2x checker command) - fix empty Code link in header table - add ex2 tagged-union exercise link --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 307dc8a commit 173f145

8 files changed

Lines changed: 428 additions & 10 deletions

File tree

book/en/src/cpp11/16-generalized-unions.md

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The size of a union is at least large enough to hold the largest data member.
1515

1616
| Book | Video | Code | X |
1717
| --- | --- | --- | --- |
18-
| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [Video Explanation]() | [Exercise Code]() | |
18+
| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [Video Explanation]() | [Exercise Code](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) | |
1919

2020
**Why introduced?**
2121

@@ -127,7 +127,56 @@ int main() {
127127
}
128128
```
129129

130-
## II. Precautions
130+
## II. Real-World Case — Generalized Unions in the STL
131+
132+
**std::variant Internal Storage — Recursive Generalized Union**
133+
> Using the `std::variant` implementation from the vendored [MSVC STL](https://github.com/mcpp-community/d2mcpp/tree/main/msvc-stl) as an example (source: [`msvc-stl/stl/inc/variant`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/variant#L343-L399)), `_CONSTEXPR20` / `_STD` are internal macros and can be ignored while reading
134+
135+
```cpp
136+
// MSVC STL · msvc-stl/stl/inc/variant (abridged)
137+
template <class _First, class... _Rest>
138+
class _Variant_storage_<false, _First, _Rest...> {
139+
public:
140+
union {
141+
remove_cv_t<_First> _Head;
142+
_Variant_storage<_Rest...> _Tail;
143+
};
144+
145+
_CONSTEXPR20 ~_Variant_storage_() {
146+
// The union does not know which member is active; destruction is
147+
// controlled by the outer variant class
148+
}
149+
// ...
150+
};
151+
```
152+
153+
`std::variant` uses a recursive union to hold multiple types in a single block of memory. The non-trivial destructor variant must explicitly define the destructor — this is exactly the key capability of C++11 generalized unions: unions can contain members with non-trivial special member functions, but their lifecycle must be managed manually
154+
155+
**std::any Small-Object Optimization — Union as Type-Erased Storage**
156+
> `std::any` uses a union to combine small-object storage, heap pointers, and raw byte buffers into the same memory (source: [`msvc-stl/stl/inc/any`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/any#L362-L376))
157+
158+
```cpp
159+
// MSVC STL · msvc-stl/stl/inc/any (abridged)
160+
class any {
161+
struct _Storage_t {
162+
union {
163+
unsigned char _TrivialData[_Any_trivial_space_size];
164+
_Small_storage_t _SmallStorage;
165+
_Big_storage_t _BigStorage;
166+
};
167+
uintptr_t _TypeData;
168+
};
169+
170+
union {
171+
_Storage_t _Storage{};
172+
max_align_t _Dummy;
173+
};
174+
};
175+
```
176+
177+
> Summary: The core storage of both `std::variant` and `std::any` relies on generalized unions. Before C++11, unions could only hold POD types, forcing the standard library to use raw byte buffers + placement new as a workaround. Generalized unions allow code to directly express "one-of-many" memory layout, with an outer wrapper responsible for tracking the active member and managing its lifecycle, making the code easier to maintain
178+
179+
## III. Precautions
131180
132181
**Accessibility**
133182
@@ -171,11 +220,21 @@ m.a = 1;
171220
double c = m.b; // Error: undefined behavior.
172221
```
173222

174-
## III. Exercise Code
223+
## IV. Exercise Code
224+
225+
### Exercise Code Topics
175226

176-
TODO
227+
- 0 - [Union Default Member Initialization](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/16-generalized-unions-0.cpp)
228+
- 1 - [Union with Non-Trivial Types and Lifecycle Management](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/16-generalized-unions-1.cpp)
229+
- 2 - [Tagged Discriminated Union — A Simplified std::variant with enum + union](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/16-generalized-unions-2.cpp)
230+
231+
### Exercise Auto-Checker Command
232+
233+
```
234+
d2x checker generalized-unions
235+
```
177236

178-
## IV. Other
237+
## V. Other
179238

180239
- [Discussion Forum](https://forum.d2learn.org/category/20)
181240
- [d2mcpp Tutorial Repository](https://github.com/mcpp-community/d2mcpp)

book/src/cpp11/16-generalized-unions.md

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
| Book | Video | Code | X |
1616
| --- | --- | --- | --- |
17-
| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [视频解读]() | [练习代码]() | |
17+
| [cppreference-union](https://cppreference.com/w/cpp/language/union.html) / [markdown](https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md) | [视频解读]() | [练习代码](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) | |
1818

1919
**为什么引入**
2020

@@ -126,7 +126,55 @@ int main() {
126126
}
127127
```
128128

129-
## 二、注意事项
129+
## 二、真实案例 - STL 中的广义联合体
130+
131+
**std::variant 的内部存储 - 递归广义联合体**
132+
> 以仓库内置的 [MSVC STL](https://github.com/mcpp-community/d2mcpp/tree/main/msvc-stl) 中的 `std::variant` 实现为例 (源码: [`msvc-stl/stl/inc/variant`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/variant#L343-L399)), `_CONSTEXPR20` / `_STD` 是库内部宏, 阅读时可忽略
133+
134+
```cpp
135+
// MSVC STL · msvc-stl/stl/inc/variant (有删节)
136+
template <class _First, class... _Rest>
137+
class _Variant_storage_<false, _First, _Rest...> {
138+
public:
139+
union {
140+
remove_cv_t<_First> _Head;
141+
_Variant_storage<_Rest...> _Tail;
142+
};
143+
144+
_CONSTEXPR20 ~_Variant_storage_() {
145+
// 联合体不知道哪个成员活跃, 析构由外层 variant 控制
146+
}
147+
// ...
148+
};
149+
```
150+
151+
`std::variant` 通过递归联合体在一块内存里容纳多个不同类型, 非平凡析构版本必须显式定义析构函数 — 这正是 C++11 广义联合体的核心能力: 联合体可以包含有非平凡特殊成员函数的成员, 但需要手动管理生命周期
152+
153+
**std::any 的小对象优化 - 联合体做类型擦除存储**
154+
> `std::any` 使用联合体将小对象、大对象指针和原始缓冲区合并到同一块内存 (源码: [`msvc-stl/stl/inc/any`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/any#L362-L376))
155+
156+
```cpp
157+
// MSVC STL · msvc-stl/stl/inc/any (有删节)
158+
class any {
159+
struct _Storage_t {
160+
union {
161+
unsigned char _TrivialData[_Any_trivial_space_size];
162+
_Small_storage_t _SmallStorage;
163+
_Big_storage_t _BigStorage;
164+
};
165+
uintptr_t _TypeData;
166+
};
167+
168+
union {
169+
_Storage_t _Storage{};
170+
max_align_t _Dummy;
171+
};
172+
};
173+
```
174+
175+
> 小结: `std::variant`、`std::any` 的核心存储都依赖广义联合体。C++11 之前联合体只能容纳 POD 类型, 标准库不得不用原始字节缓冲区 + placement new 的迂回方案; 广义联合体让代码可以直接表达"多选一"的内存布局, 并由外层封装负责跟踪活跃成员与管理生命周期, 因而更易维护
176+
177+
## 三、注意事项
130178
131179
**可访问性**
132180
@@ -170,11 +218,21 @@ m.a = 1;
170218
double c = m.b; // 错误:未定义行为
171219
```
172220

173-
## 三、练习代码
221+
## 四、练习代码
222+
223+
### 练习代码主题
174224

175-
TODO
225+
- 0 - [联合体默认成员初始化 - 最多一个变体成员可带默认初始化器](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp)
226+
- 1 - [联合体包含非平凡类型及生命周期管理 - placement new 构造 / 显式析构](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-1.cpp)
227+
- 2 - [带标签的鉴别联合体 - 用 enum + union 实现简易 variant](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-2.cpp)
228+
229+
### 练习代码自动检测命令
230+
231+
```
232+
d2x checker generalized-unions
233+
```
176234

177-
## 、其他
235+
## 、其他
178236

179237
- [交流讨论](https://forum.d2learn.org/category/20)
180238
- [d2mcpp教程仓库](https://github.com/mcpp-community/d2mcpp)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// d2mcpp: https://github.com/mcpp-community/d2mcpp
2+
// license: Apache-2.0
3+
// file: dslings/cpp11/16-generalized-unions-2.cpp
4+
//
5+
// Exercise/练习: cpp11 | 16 - generalized unions | 带标签的鉴别联合体
6+
//
7+
// Tips/提示:
8+
// - 使用 enum 标记联合体中哪个成员是活跃的
9+
// - 根据标签决定构造哪个成员、析构哪个成员
10+
// - 联合体本身不知道哪个成员活跃, 标签由外层结构体维护
11+
//
12+
// Docs/文档:
13+
// - https://cppreference.com/w/cpp/language/union.html
14+
// - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md
15+
//
16+
// 练习交流讨论: http://forum.d2learn.org/category/20
17+
//
18+
// Auto-Checker/自动检测命令:
19+
//
20+
// d2x checker generalized-unions
21+
//
22+
23+
#include <d2x/cpp/common.hpp>
24+
#include <string>
25+
#include <new>
26+
27+
// 标签类型 — 标记联合体中哪个成员是活跃的
28+
enum class Tag {
29+
D2X_YOUR_ANSWER, // 整数活跃
30+
STRING // 字符串活跃
31+
};
32+
33+
// 联合体 — 包含 int 和 std::string, 需手动管理生命周期
34+
union Data {
35+
int i;
36+
std::string s;
37+
38+
Data() : i(0) {}
39+
~Data() {} // 析构由外层根据 tag 决定
40+
};
41+
42+
// 带标签的鉴别联合体
43+
struct Value {
44+
Tag tag;
45+
Data data;
46+
47+
// 构造 int
48+
Value(int val) : tag(D2X_YOUR_ANSWER) {
49+
new (&data.i) int(val);
50+
}
51+
52+
// 构造 string
53+
Value(const std::string& val) : tag(Tag::STRING) {
54+
D2X_YOUR_ANSWER;
55+
}
56+
57+
// 获取 int 成员 — 仅当 tag == INTEGER 时合法
58+
int as_int() {
59+
return data.i;
60+
}
61+
62+
// 获取 string 成员 — 仅当 tag == STRING 时合法
63+
const std::string& as_string() {
64+
return data.s;
65+
}
66+
67+
// 析构 — 根据 tag 决定析构哪个成员
68+
~Value() {
69+
if (tag == Tag::STRING) {
70+
D2X_YOUR_ANSWER;
71+
}
72+
}
73+
};
74+
75+
76+
int main() {
77+
78+
// 1. 构造 int 值并验证
79+
Value v1(42);
80+
d2x_assert(v1.tag == Tag::INTEGER);
81+
d2x_assert_eq(v1.as_int(), 42);
82+
83+
// 2. 构造 string 值并验证
84+
Value v2(std::string("hello"));
85+
d2x_assert(v2.tag == Tag::STRING);
86+
d2x_assert(v2.as_string() == "hello");
87+
88+
// 3. 从 string 切换到 int
89+
{
90+
Value v3(std::string("world"));
91+
d2x_assert(v3.as_string() == "world");
92+
93+
// 手动析构 string 成员, 切换到 int
94+
v3.data.s.~basic_string();
95+
v3.tag = Tag::INTEGER;
96+
v3.data.i = 100;
97+
98+
d2x_assert_eq(v3.as_int(), D2X_YOUR_ANSWER);
99+
100+
// 离开作用域时 ~Value() 发现 tag == INTEGER, 不析构 string
101+
}
102+
D2X_WAIT
103+
104+
return 0;
105+
}

dslings/cpp11/xmake.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ target("cpp11-16-generalized-unions-0")
180180
target("cpp11-16-generalized-unions-1")
181181
add_files("16-generalized-unions-1.cpp")
182182

183+
target("cpp11-16-generalized-unions-2")
184+
add_files("16-generalized-unions-2.cpp")
185+
183186
-- target: cpp11-17-pod-type
184187

185188
target("cpp11-17-pod-type-0")

0 commit comments

Comments
 (0)