C++中将仅含POD成员的包装结构体数组转为POD数组是否合法?
结论:你给出的代码属于未定义行为(UB),但仅当你将转换后的指针当作
Data数组访问时才会触发,仅访问首元素是合法的。 首先明确前提:你的Wrapper属于C++标准定义的标准布局类:
- 没有虚函数、虚基类
- 所有非静态数据成员的访问控制一致(struct默认public,仅包含
Data一个非静态成员) - 第一个非静态数据成员
data是POD类型(本身也是标准布局)
根据C++标准规定,标准布局类的对象指针,和指向其第一个非静态数据成员的指针可以互相reinterpret_cast转换,且保证得到有效地址:
对于标准布局类型T的对象x,如果x的第一个非静态数据成员类型是U,那么
reinterpret_cast<U*>(&x)一定是合法的,且能正确得到指向该成员的指针。
因此你如果仅用转换后的指针访问首元素:
Data* first_data = reinterpret_cast<Data*>(vec_of_wrappers.data()); // 仅访问 *first_data 是合法的
这个操作本身没有问题。
为什么当作数组用就是UB?
你代码中得到array_of_data后,如果执行类似array_of_data[1]、array_of_data + 2这类指针算术操作,100%属于未定义行为,原因有二:
- 指针算术的生效范围限制:C++标准仅允许在同一个数组的元素范围内执行指针算术。你原始的
vec_of_wrappers.data()指向的是Wrapper类型的数组,每个元素是Wrapper对象,不是Data对象。哪怕Wrapper和Data的大小完全一致、内存布局没有任何填充,标准也不认可你可以把Wrapper数组当成Data数组做指针偏移。 - 严格别名规则冲突:C++的严格别名规则要求,不能通过不相关类型的指针访问同一个对象。你如果通过
Data*指针访问原本属于Wrapper数组的后续元素,编译器的优化逻辑可能会认为这两个指针指向的内存没有关联,做出错误的优化,最终导致运行结果异常。
实现需求的合法方案
你要包装不可修改的POD类型、新增辅助函数、同时尽可能兼容原始POD的使用,有几个完全合法的实现方式:
- 给
Wrapper添加隐式转换运算符:
这样任意struct Wrapper { Data data; operator Data&() noexcept { return data; } operator const Data&() const noexcept { return data; } // 其余辅助函数不变 };Wrapper对象都可以自动转换成Data的引用,单个使用时和原始Data完全没有区别。 - 如果确实需要连续的
Data数组,直接遍历vector<Wrapper>逐个取出data成员的地址即可,这个操作没有任何额外性能开销。 - 单个
Wrapper和Data之间的指针转换始终是合法的,不要对整个数组做转换即可。
内容的提问来源于stack exchange,提问作者Ocelot
相关产品推荐
相关产品推荐

