Skip to content
Merged
67 changes: 62 additions & 5 deletions book/src/cpp11/16-generalized-unions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | |

**为什么引入**

Expand Down Expand Up @@ -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 _First, class... _Rest>
class _Variant_storage_<false, _First, _Rest...> {
public:
union {
remove_cv_t<_First> _Head;
_Variant_storage<_Rest...> _Tail;
};

_CONSTEXPR20 ~_Variant_storage_() noexcept {
// 联合体不知道哪个成员活跃, 析构由外层 variant 控制
}
Comment thread
lczllx marked this conversation as resolved.
Outdated
// ...
};
```

`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 的迂回方案; 广义联合体让代码可以直接表达"多选一"的语义, 类型安全且更易维护
Comment thread
lczllx marked this conversation as resolved.
Outdated

## 三、注意事项

**可访问性**

Expand Down Expand Up @@ -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)
- 1 - [联合体包含非平凡类型及生命周期管理 - placement new 构造 / 显式析构](https://github.com/mcpp-community/d2mcpp/blob/main/dslings/cpp11/16-generalized-unions-1.cpp)

### 练习代码自动检测命令

```
d2x checker generalized-unions
```

## 、其他
## 、其他

- [交流讨论](https://forum.d2learn.org/category/20)
- [d2mcpp教程仓库](https://github.com/mcpp-community/d2mcpp)
Expand Down