为什么C++中可同时访问union的两个成员?该写法是否合法?
核心原理是RapidJSON对union的内存布局做了特殊设计,利用了主流编译器的通用实现兼容特性,具体逻辑如下:
1. 内存布局的刻意设计
RapidJSON在定义Data union时,保证所有成员的最后2个字节永远不会存储实际业务数据,专门预留出来存储类型标记位:
Flag结构体的payload数组长度被特意设置为整个Dataunion的大小减去2字节,因此Flag的最后一个字段uint16_t flags永远固定在整个union内存的最后2字节位置- 其他
Dataunion的成员(包括Number类型的n)的内存布局都不会用到最后2字节的空间,存储数值时只会覆盖前N个字节,和末尾的标记位空间完全不重叠
2. 跨union成员访问的本质
你看到的同时访问data_.n和data_.f的操作,本质上是通过不同的union成员类型,访问同一块内存的不同位置,不存在互相覆盖的问题:
- 给
data_.n.i64赋值时,修改的是union前半段的数值存储区域 - 给
data_.f.flags赋值/读取时,操作的是union末尾2字节的预留标记区域
两者操作的内存范围完全不重合,自然不会出现数据冲突。
3. 标准兼容说明
严格从C++标准定义来看,这种同时访问多个union成员的行为确实属于未定义行为:标准规定union任意时刻只能有一个活跃成员,访问非活跃成员是不符合规范的。
但这种内存访问模式属于几乎所有主流编译器都支持的实现定义行为,本质上就是通过不同的类型视图访问已知布局的内存区域,只要内存布局设计正确就不会出问题。RapidJSON作为高性能JSON库,用这种设计把类型标记位直接塞到union本身的预留空间里,避免了在union外部单独存储标记位的额外内存开销,是典型的性能向设计取舍。
内容的提问来源于stack exchange,提问作者yangsuli
相关产品推荐
相关产品推荐

