Skip to content

Commit f71b02b

Browse files
authored
Fix wrong statement in code sample in (#75)
1 parent 473c183 commit f71b02b

1 file changed

Lines changed: 26 additions & 5 deletions

File tree

documents/vol3-standard-library/12-object-size-and-trivial-types.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ struct alignas(16) Vec4 {
8080
C++ 标准把一组「类型属性」拆开来,精确表达「这个类型的对象在内存里怎么行为」。这是 C++11 的设计(把历史上的 POD 拆成几件事)。先把几个常被混淆的词摆清楚:
8181

8282
- **trivial(平凡)类型**:特殊成员(默认构造、拷贝/移动构造、赋值、析构)都是编译器生成的、没有自定义逻辑。换句话说,构造/拷贝/析构不产生任何运行时代码——对象的比特位就是它的全部,没有隐藏动作。
83-
- **trivially_copyable(可平凡拷贝)类型**:可以安全地用 `memcpy` 按字节拷贝(拷完目标有同样的对象表示,且能正常析构)。**这是能否用 `memcpy` 的判据**
83+
- **trivially_copyable(可平凡拷贝)类型**:可以安全地用 `memcpy` 按字节拷贝(拷完目标有同样的对象表示,且能正常析构)。**这是能否用 `memcpy` 的判据**。根据C++23草案:
84+
<https://eel.is/c++draft/class.prop#11>, 大致有三个要求:
85+
1. 至少有一个拷贝/移动构造或赋值操作不是 deleted
86+
2. 所有存在的(eligible)拷贝/移动构造函数和拷贝/移动赋值运算符都是 trivial.
87+
3. 析构函数是 trivial.
8488
- **standard-layout(标准布局)类型**:有可预测的内存布局规则(成员按声明顺序排布、没有复杂的访问控制 / 虚继承 / 多重基类导致的不确定布局)。**这是能否和 C struct 布局兼容的判据**
8589

8690
一个关键事实:老概念 `POD`(Plain Old Data)在 C++11 被拆成了 `trivial``standard-layout``POD` 在语义上就是「既 trivial 又 standard-layout」。所以那些和 ABI、C 互操作相关的安全假设,现在用 `std::is_standard_layout_v<T>``std::is_trivially_copyable_v<T>` 分别检查。
@@ -101,12 +105,29 @@ static_assert(std::is_standard_layout_v<S>);
101105
对比一个非平凡的:
102106
103107
```cpp
104-
struct T {
105-
T() { /* 自定义构造 */ }
108+
struct T_0 {
106109
int x;
107110
};
108-
// T 不是 trivial(用户定义了构造),通常也不是 trivially_copyable
109-
static_assert(!std::is_trivial_v<T>);
111+
// T_0 是 trivial且trivially_copyable
112+
static_assert(std::is_trivial_v<T_0>);
113+
static_assert(std::is_trivially_copyable_v<T_0>);
114+
115+
struct T_1 {
116+
T_1() { /* 自定义构造 */ }
117+
int x;
118+
};
119+
// T 不是 trivial(用户定义了构造),但在这个例子中是trivially_copyable
120+
// 注意,默认构造函数完全不在检查范围内。
121+
static_assert(!std::is_trivial_v<T_1>);
122+
static_assert(std::is_trivially_copyable_v<T_1>);
123+
124+
struct T_2 {
125+
T_2() {}
126+
T_2(const T_2&) { /* 自定义拷贝构造 */ }
127+
int x;
128+
};
129+
static_assert(!std::is_trivial_v<T_2>);
130+
static_assert(!std::is_trivially_copyable_v<T_2>);
110131
```
111132

112133
再强调一条易错的:**trivial ≠ trivially_copyable**,前者强调特殊成员(尤其默认构造)的「平凡性」,后者强调按字节复制是否安全。判断能不能 `memcpy`,用 `std::is_trivially_copyable_v<T>`,别用 `is_trivial`

0 commit comments

Comments
 (0)