在标准布局类联合结构中对this执行reinterpret_cast是否为未定义行为?
咱们先看一下你给出的代码,运行时test()确实会返回10,但核心疑问是:当A满足StandardLayoutType概念时,B::c里把this转换成uint32_t*去获取A::a的行为,到底算不算未定义行为?
#include <cstdint> struct B { uint32_t c() { uint32_t * value = reinterpret_cast<uint32_t *>(this); return * value; } }; struct A { union { B b1; B b2; uint32_t a { 10 }; }; }; int test() { A a; return a.b1.c(); }
关键规则拆解
要回答这个问题,我们需要结合C++标准里的几个核心规则:
标准布局联合的内存特性
当A是标准布局类型时,它的匿名联合成员也会是标准布局。联合的本质是所有成员共享同一内存起始地址,并且整个联合的大小是最大成员的大小(这里是uint32_t的4字节),同时满足所有成员的对齐要求(uint32_t的4字节对齐)。所以&a.b1、&a.b2和&a.a的地址完全相同,且都是4字节对齐的。指针转换的合法性
reinterpret_cast<uint32_t*>(this)这个转换是允许的:this指向a.b1,而a.b1的地址是4字节对齐的,符合uint32_t*的对齐要求;- 标准允许在标准布局类型的指针之间进行
reinterpret_cast,只要地址对齐且转换后的指针指向的内存区域有效。
类型别名规则与对象访问
这里的核心是:我们解引用uint32_t*时,实际上是在访问联合的活跃成员a.a(因为A的构造初始化了a,所以a.a是当前联合中活跃的对象)。而我们用uint32_t*访问uint32_t类型的对象,完全符合C++的类型别名规则(直接访问对象的动态类型),不存在违规。内存访问的有效性
虽然B本身是没有非静态数据成员的空结构体(大小为1字节),但它作为联合的成员,所在的内存区域是联合分配的4字节空间(属于A对象的内存)。解引用uint32_t*访问的4字节完全在A对象的内存范围内,不存在越界访问的问题。
结论
当A满足StandardLayoutType概念时,B::c中的指针转换和解引用行为不是未定义行为,是符合标准的定义良好的操作。
内容的提问来源于stack exchange,提问作者Vladislav Ivanov

