From 2dae6108f88fc6e9a7a006da5b9d1ac520385cb2 Mon Sep 17 00:00:00 2001 From: li-zhou <2181719471@qq.com> Date: Thu, 11 Jun 2026 21:50:31 +0800 Subject: [PATCH 1/6] docs(cpp11): add STL real-world case and exercise mapping for 16-generalized-unions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- book/src/cpp11/16-generalized-unions.md | 67 +++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/book/src/cpp11/16-generalized-unions.md b/book/src/cpp11/16-generalized-unions.md index 0c771c7..5fc07f6 100644 --- a/book/src/cpp11/16-generalized-unions.md +++ b/book/src/cpp11/16-generalized-unions.md @@ -14,7 +14,7 @@ | Book | Video | Code | X | | --- | --- | --- | --- | -| [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) | [视频解读]() | [练习代码]() | | +| [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) | | **为什么引入** @@ -126,7 +126,55 @@ int main() { } ``` -## 二、注意事项 +## 二、真实案例 - STL 中的广义联合体 + +**std::variant 的内部存储 - 递归广义联合体** +> 以仓库内置的 [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` 是库内部宏, 阅读时可忽略 + +```cpp +// MSVC STL · msvc-stl/stl/inc/variant (有删节) +template +class _Variant_storage_ { +public: + union { + remove_cv_t<_First> _Head; + _Variant_storage<_Rest...> _Tail; + }; + + _CONSTEXPR20 ~_Variant_storage_() noexcept { + // 联合体不知道哪个成员活跃, 析构由外层 variant 控制 + } + // ... +}; +``` + +`std::variant` 通过递归联合体在一块内存里容纳多个不同类型, 非平凡析构版本必须显式定义析构函数 — 这正是 C++11 广义联合体的核心能力: 联合体可以包含有非平凡特殊成员函数的成员, 但需要手动管理生命周期 + +**std::any 的小对象优化 - 联合体做类型擦除存储** +> `std::any` 使用联合体将小对象、大对象指针和原始缓冲区合并到同一块内存 (源码: [`msvc-stl/stl/inc/any`](https://github.com/mcpp-community/d2mcpp/blob/main/msvc-stl/stl/inc/any#L362-L376)) + +```cpp +// MSVC STL · msvc-stl/stl/inc/any (有删节) +class any { + struct _Storage_t { + union { + unsigned char _TrivialData[_Any_trivial_space_size]; + _Small_storage_t _SmallStorage; + _Big_storage_t _BigStorage; + }; + uintptr_t _TypeData; + }; + + union { + _Storage_t _Storage{}; + max_align_t _Dummy; + }; +}; +``` + +> 小结: `std::variant`、`std::any` 的核心存储都依赖广义联合体。C++11 之前联合体只能容纳 POD 类型, 标准库不得不用原始字节缓冲区 + placement new 的迂回方案; 广义联合体让代码可以直接表达"多选一"的语义, 类型安全且更易维护 + +## 三、注意事项 **可访问性** @@ -170,11 +218,20 @@ m.a = 1; double c = m.b; // 错误:未定义行为 ``` -## 三、练习代码 +## 四、练习代码 + +### 练习代码主题 -TODO +- 0 - [联合体默认成员初始化](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) — C++11 规则: 最多一个变体成员可带默认初始化器 +- 1 - [联合体包含非平凡类型及生命周期管理](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-1.cpp) — placement new 构造 / 显式析构 / 非平凡成员的正确访问 + +### 练习代码自动检测命令 + +``` +d2x checker generalized-unions +``` -## 四、其他 +## 五、其他 - [交流讨论](https://forum.d2learn.org/category/20) - [d2mcpp教程仓库](https://github.com/mcpp-community/d2mcpp) From 99fc7857d9697089e9192a4c119157455f07fb0e Mon Sep 17 00:00:00 2001 From: li-zhou <2181719471@qq.com> Date: Thu, 11 Jun 2026 22:25:58 +0800 Subject: [PATCH 2/6] docs(cpp11): fix exercise list format to match project convention --- book/src/cpp11/16-generalized-unions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/cpp11/16-generalized-unions.md b/book/src/cpp11/16-generalized-unions.md index 5fc07f6..bc76368 100644 --- a/book/src/cpp11/16-generalized-unions.md +++ b/book/src/cpp11/16-generalized-unions.md @@ -222,8 +222,8 @@ double c = m.b; // 错误:未定义行为 ### 练习代码主题 -- 0 - [联合体默认成员初始化](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) — C++11 规则: 最多一个变体成员可带默认初始化器 -- 1 - [联合体包含非平凡类型及生命周期管理](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-1.cpp) — placement new 构造 / 显式析构 / 非平凡成员的正确访问 +- 0 - [联合体默认成员初始化 - 最多一个变体成员可带默认初始化器](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) +- 1 - [联合体包含非平凡类型及生命周期管理 - placement new 构造 / 显式析构](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-1.cpp) ### 练习代码自动检测命令 From 359c65309c960d405a383e14a4c47dd9cfe581c0 Mon Sep 17 00:00:00 2001 From: Dragon Roar <2181719471@qq.com> Date: Fri, 12 Jun 2026 08:41:06 +0800 Subject: [PATCH 3/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- book/src/cpp11/16-generalized-unions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/cpp11/16-generalized-unions.md b/book/src/cpp11/16-generalized-unions.md index bc76368..b6a0db0 100644 --- a/book/src/cpp11/16-generalized-unions.md +++ b/book/src/cpp11/16-generalized-unions.md @@ -141,7 +141,7 @@ public: _Variant_storage<_Rest...> _Tail; }; - _CONSTEXPR20 ~_Variant_storage_() noexcept { + _CONSTEXPR20 ~_Variant_storage_() { // 联合体不知道哪个成员活跃, 析构由外层 variant 控制 } // ... From d75b167528a81546ce7fe08bf3a0856b5b9a9166 Mon Sep 17 00:00:00 2001 From: li-zhou <2181719471@qq.com> Date: Fri, 12 Jun 2026 09:03:41 +0800 Subject: [PATCH 4/6] =?UTF-8?q?docs(cpp11):=20apply=20Copilot=20review=20s?= =?UTF-8?q?uggestions=20=E2=80=94=20remove=20inaccurate=20noexcept,=20fix?= =?UTF-8?q?=20=E7=B1=BB=E5=9E=8B=E5=AE=89=E5=85=A8=20wording?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/src/cpp11/16-generalized-unions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/cpp11/16-generalized-unions.md b/book/src/cpp11/16-generalized-unions.md index b6a0db0..9c725ad 100644 --- a/book/src/cpp11/16-generalized-unions.md +++ b/book/src/cpp11/16-generalized-unions.md @@ -172,7 +172,7 @@ class any { }; ``` -> 小结: `std::variant`、`std::any` 的核心存储都依赖广义联合体。C++11 之前联合体只能容纳 POD 类型, 标准库不得不用原始字节缓冲区 + placement new 的迂回方案; 广义联合体让代码可以直接表达"多选一"的语义, 类型安全且更易维护 +> 小结: `std::variant`、`std::any` 的核心存储都依赖广义联合体。C++11 之前联合体只能容纳 POD 类型, 标准库不得不用原始字节缓冲区 + placement new 的迂回方案; 广义联合体让代码可以直接表达"多选一"的内存布局, 并由外层封装负责跟踪活跃成员与管理生命周期, 因而更易维护 ## 三、注意事项 From a378028aef2dad18f70b94382dc72574bbf000f5 Mon Sep 17 00:00:00 2001 From: li-zhou <2181719471@qq.com> Date: Thu, 11 Jun 2026 23:00:44 +0800 Subject: [PATCH 5/6] feat(cpp11): add tagged-union exercise for 16-generalized-unions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- book/src/cpp11/16-generalized-unions.md | 1 + dslings/cpp11/16-generalized-unions-2.cpp | 106 +++++++++++++++++++ dslings/cpp11/xmake.lua | 3 + dslings/en/cpp11/16-generalized-unions-2.cpp | 106 +++++++++++++++++++ dslings/en/cpp11/xmake.lua | 3 + solutions/cpp11/16-generalized-unions-2.cpp | 82 ++++++++++++++ solutions/cpp11/xmake.lua | 3 + 7 files changed, 304 insertions(+) create mode 100644 dslings/cpp11/16-generalized-unions-2.cpp create mode 100644 dslings/en/cpp11/16-generalized-unions-2.cpp create mode 100644 solutions/cpp11/16-generalized-unions-2.cpp diff --git a/book/src/cpp11/16-generalized-unions.md b/book/src/cpp11/16-generalized-unions.md index 9c725ad..3934262 100644 --- a/book/src/cpp11/16-generalized-unions.md +++ b/book/src/cpp11/16-generalized-unions.md @@ -224,6 +224,7 @@ double c = m.b; // 错误:未定义行为 - 0 - [联合体默认成员初始化 - 最多一个变体成员可带默认初始化器](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-0.cpp) - 1 - [联合体包含非平凡类型及生命周期管理 - placement new 构造 / 显式析构](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-1.cpp) +- 2 - [带标签的鉴别联合体 - 用 enum + union 实现简易 variant](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-2.cpp) ### 练习代码自动检测命令 diff --git a/dslings/cpp11/16-generalized-unions-2.cpp b/dslings/cpp11/16-generalized-unions-2.cpp new file mode 100644 index 0000000..906285e --- /dev/null +++ b/dslings/cpp11/16-generalized-unions-2.cpp @@ -0,0 +1,106 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// file: dslings/cpp11/16-generalized-unions-2.cpp +// +// Exercise/练习: cpp11 | 16 - generalized unions | 带标签的鉴别联合体 +// +// Tips/提示: +// - 使用 enum 标记联合体中哪个成员是活跃的 +// - 根据标签决定构造哪个成员、析构哪个成员 +// - 联合体本身不知道哪个成员活跃, 标签由外层结构体维护 +// +// Docs/文档: +// - https://cppreference.com/w/cpp/language/union.html +// - https://github.com/mcpp-community/d2mcpp/blob/main/book/src/cpp11/16-generalized-unions.md +// +// 练习交流讨论: http://forum.d2learn.org/category/20 +// +// Auto-Checker/自动检测命令: +// +// d2x checker generalized-unions +// + +#include +#include +#include + +// 标签类型 — 标记联合体中哪个成员是活跃的 +enum class Tag { + D2X_YOUR_ANSWER, // 整数活跃 + STRING // 字符串活跃 +}; + +// 联合体 — 包含 int 和 std::string, 需手动管理生命周期 +union Data { + int i; + std::string s; + + Data() : i(0) {} + ~Data() {} // 析构由外层根据 tag 决定 +}; + +// 带标签的鉴别联合体 +struct Value { + Tag tag; + Data data; + + // 构造 int + Value(int val) : tag(D2X_YOUR_ANSWER) { + new (&data.i) int(val); + } + + // 构造 string + Value(const std::string& val) : tag(Tag::STRING) { + D2X_YOUR_ANSWER; + } + + // 获取 int 成员 — 仅当 tag == INTEGER 时合法 + int as_int() { + return data.i; + } + + // 获取 string 成员 — 仅当 tag == STRING 时合法 + const std::string& as_string() { + return data.s; + } + + // 析构 — 根据 tag 决定析构哪个成员 + ~Value() { + if (tag == Tag::STRING) { + D2X_YOUR_ANSWER; + } + } +}; + + +int main() { + + // 1. 构造 int 值并验证 + Value v1(42); + d2x_assert(v1.tag == Tag::INTEGER); + d2x_assert_eq(v1.as_int(), 42); + + // 2. 构造 string 值并验证 + Value v2(std::string("hello")); + d2x_assert(v2.tag == Tag::STRING); + d2x_assert(v2.as_string() == "hello"); + + // 3. 从 string 切换到 int + { + Value v3(std::string("world")); + d2x_assert(v3.as_string() == "world"); + + // 手动析构 string 成员, 切换到 int + v3.data.s.~basic_string(); + v3.tag = Tag::INTEGER; + v3.data.i = 100; + + d2x_assert_eq(v3.as_int(), D2X_YOUR_ANSWER); + + // 离开作用域时 ~Value() 发现 tag == INTEGER, 不析构 string + } + + D2X_WAIT + + return 0; +} diff --git a/dslings/cpp11/xmake.lua b/dslings/cpp11/xmake.lua index 3f7d247..e7dbdae 100644 --- a/dslings/cpp11/xmake.lua +++ b/dslings/cpp11/xmake.lua @@ -180,6 +180,9 @@ target("cpp11-16-generalized-unions-0") target("cpp11-16-generalized-unions-1") add_files("16-generalized-unions-1.cpp") +target("cpp11-16-generalized-unions-2") + add_files("16-generalized-unions-2.cpp") + -- target: cpp11-17-pod-type target("cpp11-17-pod-type-0") diff --git a/dslings/en/cpp11/16-generalized-unions-2.cpp b/dslings/en/cpp11/16-generalized-unions-2.cpp new file mode 100644 index 0000000..2759e37 --- /dev/null +++ b/dslings/en/cpp11/16-generalized-unions-2.cpp @@ -0,0 +1,106 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// file: dslings/en/cpp11/16-generalized-unions-2.cpp +// +// Exercise: cpp11 | 16 - generalized unions | tagged discriminated union +// +// Tips: +// - Use an enum to tag which member of the union is active +// - Choose constructor/destructor based on the tag +// - The union itself cannot track which member is active; the tag is maintained externally +// +// Docs: +// - https://cppreference.com/w/cpp/language/union.html +// - https://github.com/mcpp-community/d2mcpp/blob/main/book/en/src/cpp11/16-generalized-unions.md +// +// Discussion Forum: http://forum.d2learn.org/category/20 +// +// Auto-Checker: +// +// d2x checker generalized-unions +// + +#include +#include +#include + +// Tag type — marks which member of the union is active +enum class Tag { + D2X_YOUR_ANSWER,// integer is active + STRING // string is active +}; + +// Union — contains int and std::string, lifecycle managed externally +union Data { + int i; + std::string s; + + Data() : i(0) {} + ~Data() {} // destruction decided by outer struct based on tag +}; + +// Tagged discriminated union +struct Value { + Tag tag; + Data data; + + // Construct with int + Value(int val) : tag(D2X_YOUR_ANSWER) { + new (&data.i) int(val); + } + + // Construct with string + Value(const std::string& val) : tag(Tag::STRING) { + D2X_YOUR_ANSWER; + } + + // Access int member — valid only when tag == INTEGER + int as_int() { + return data.i; + } + + // Access string member — valid only when tag == STRING + const std::string& as_string() { + return data.s; + } + + // Destructor — destroy the active member based on tag + ~Value() { + if (tag == Tag::STRING) { + D2X_YOUR_ANSWER; + } + } +}; + + +int main() { + + // 1. Construct an int value and verify + Value v1(42); + d2x_assert(v1.tag == Tag::INTEGER); + d2x_assert_eq(v1.as_int(), 42); + + // 2. Construct a string value and verify + Value v2(std::string("hello")); + d2x_assert(v2.tag == Tag::STRING); + d2x_assert(v2.as_string() == "hello"); + + // 3. Switch from string to int + { + Value v3(std::string("world")); + d2x_assert(v3.as_string() == "world"); + + // Manually destroy the string member, then switch to int + v3.data.s.~basic_string(); + v3.tag = Tag::INTEGER; + v3.data.i = 100; + + d2x_assert_eq(v3.as_int(), D2X_YOUR_ANSWER); + + // When v3 goes out of scope, ~Value() sees tag == INTEGER, skips string destruction + } + + D2X_WAIT + + return 0; +} diff --git a/dslings/en/cpp11/xmake.lua b/dslings/en/cpp11/xmake.lua index 3f60434..6daeb9e 100644 --- a/dslings/en/cpp11/xmake.lua +++ b/dslings/en/cpp11/xmake.lua @@ -177,6 +177,9 @@ target("cpp11-16-generalized-unions-0") target("cpp11-16-generalized-unions-1") add_files("16-generalized-unions-1.cpp") +target("cpp11-16-generalized-unions-2") + add_files("16-generalized-unions-2.cpp") + -- target: cpp11-17-pod-type target("cpp11-17-pod-type-0") diff --git a/solutions/cpp11/16-generalized-unions-2.cpp b/solutions/cpp11/16-generalized-unions-2.cpp new file mode 100644 index 0000000..4f4a067 --- /dev/null +++ b/solutions/cpp11/16-generalized-unions-2.cpp @@ -0,0 +1,82 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/16-generalized-unions-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/16-generalized-unions-2.cpp +// + +#include +#include +#include + +enum class Tag { + INTEGER, + STRING +}; + +union Data { + int i; + std::string s; + + Data() : i(0) {} + ~Data() {} +}; + +struct Value { + Tag tag; + Data data; + + Value(int val) : tag(Tag::INTEGER) { + new (&data.i) int(val); + } + + Value(const std::string& val) : tag(Tag::STRING) { + new (&data.s) std::string(val); + } + + int as_int() { + return data.i; + } + + const std::string& as_string() { + return data.s; + } + + ~Value() { + if (tag == Tag::STRING) { + data.s.~basic_string(); + } + } +}; + + +int main() { + + // 1. 构造 int 值并验证 + Value v1(42); + d2x_assert(v1.tag == Tag::INTEGER); + d2x_assert_eq(v1.as_int(), 42); + + // 2. 构造 string 值并验证 + Value v2(std::string("hello")); + d2x_assert(v2.tag == Tag::STRING); + d2x_assert(v2.as_string() == "hello"); + + // 3. 从 string 切换到 int + { + Value v3(std::string("world")); + d2x_assert(v3.as_string() == "world"); + + // 手动析构 string 成员, 切换到 int + v3.data.s.~basic_string(); + v3.tag = Tag::INTEGER; + v3.data.i = 100; + + d2x_assert_eq(v3.as_int(), 100); + + // 离开作用域时 ~Value() 发现 tag == INTEGER, 不析构 string + } + + return 0; +} diff --git a/solutions/cpp11/xmake.lua b/solutions/cpp11/xmake.lua index 6cfcfce..31cdd51 100644 --- a/solutions/cpp11/xmake.lua +++ b/solutions/cpp11/xmake.lua @@ -183,6 +183,9 @@ target("cpp11-16-generalized-unions-0-ref") target("cpp11-16-generalized-unions-1-ref") add_files("16-generalized-unions-1.cpp") +target("cpp11-16-generalized-unions-2-ref") + add_files("16-generalized-unions-2.cpp") + -- target: cpp11-17-pod-type target("cpp11-17-pod-type-0-ref") From eef9661daeec08b6d5815832b540de5386ac3f08 Mon Sep 17 00:00:00 2001 From: li-zhou <2181719471@qq.com> Date: Sat, 13 Jun 2026 19:43:00 +0800 Subject: [PATCH 6/6] 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 --- book/en/src/cpp11/16-generalized-unions.md | 69 ++++++++++++++++++-- dslings/cpp11/16-generalized-unions-2.cpp | 1 - dslings/en/cpp11/16-generalized-unions-2.cpp | 3 +- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/book/en/src/cpp11/16-generalized-unions.md b/book/en/src/cpp11/16-generalized-unions.md index 27fb0a2..26ef95c 100644 --- a/book/en/src/cpp11/16-generalized-unions.md +++ b/book/en/src/cpp11/16-generalized-unions.md @@ -15,7 +15,7 @@ The size of a union is at least large enough to hold the largest data member. | Book | Video | Code | X | | --- | --- | --- | --- | -| [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]() | | +| [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) | | **Why introduced?** @@ -127,7 +127,56 @@ int main() { } ``` -## II. Precautions +## II. Real-World Case — Generalized Unions in the STL + +**std::variant Internal Storage — Recursive Generalized Union** +> 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 + +```cpp +// MSVC STL · msvc-stl/stl/inc/variant (abridged) +template +class _Variant_storage_ { +public: + union { + remove_cv_t<_First> _Head; + _Variant_storage<_Rest...> _Tail; + }; + + _CONSTEXPR20 ~_Variant_storage_() { + // The union does not know which member is active; destruction is + // controlled by the outer variant class + } + // ... +}; +``` + +`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 + +**std::any Small-Object Optimization — Union as Type-Erased Storage** +> `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)) + +```cpp +// MSVC STL · msvc-stl/stl/inc/any (abridged) +class any { + struct _Storage_t { + union { + unsigned char _TrivialData[_Any_trivial_space_size]; + _Small_storage_t _SmallStorage; + _Big_storage_t _BigStorage; + }; + uintptr_t _TypeData; + }; + + union { + _Storage_t _Storage{}; + max_align_t _Dummy; + }; +}; +``` + +> 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 + +## III. Precautions **Accessibility** @@ -171,11 +220,21 @@ m.a = 1; double c = m.b; // Error: undefined behavior. ``` -## III. Exercise Code +## IV. Exercise Code + +### Exercise Code Topics -TODO +- 0 - [Union Default Member Initialization](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/en/cpp11/16-generalized-unions-0.cpp) +- 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) +- 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) + +### Exercise Auto-Checker Command + +``` +d2x checker generalized-unions +``` -## IV. Other +## V. Other - [Discussion Forum](https://forum.d2learn.org/category/20) - [d2mcpp Tutorial Repository](https://github.com/mcpp-community/d2mcpp) diff --git a/dslings/cpp11/16-generalized-unions-2.cpp b/dslings/cpp11/16-generalized-unions-2.cpp index 906285e..57bd336 100644 --- a/dslings/cpp11/16-generalized-unions-2.cpp +++ b/dslings/cpp11/16-generalized-unions-2.cpp @@ -99,7 +99,6 @@ int main() { // 离开作用域时 ~Value() 发现 tag == INTEGER, 不析构 string } - D2X_WAIT return 0; diff --git a/dslings/en/cpp11/16-generalized-unions-2.cpp b/dslings/en/cpp11/16-generalized-unions-2.cpp index 2759e37..5cd28dc 100644 --- a/dslings/en/cpp11/16-generalized-unions-2.cpp +++ b/dslings/en/cpp11/16-generalized-unions-2.cpp @@ -98,8 +98,7 @@ int main() { d2x_assert_eq(v3.as_int(), D2X_YOUR_ANSWER); // When v3 goes out of scope, ~Value() sees tag == INTEGER, skips string destruction - } - + } D2X_WAIT return 0;