C++的 reinterpret_cast,以及放置new之后的严格别名规则
以下代码在C++标准下是未定义行为(UB),但许多类数组容器(如 std::vector)都是这样实现的,先使用 placement-new 再使用 reinterpret_cast<T*>。
但根据C++的严格别名规则(参见 https://en.cppreference.com/cpp/language/reinterpret_cast),这是UB。
- p0532r0表示
std::launder()不能解决这个问题; std::start_lifetime_as()似乎也不能解决这个问题;- p0593r6 ?
struct T { int x; };
alignas(T) std::byte buffer[sizeof(T)];
// 1. Placement new to create the object
new(buffer) T{42}; // #obj1
// question: how to get `#obj1` by buffer pointer ?
T *t = reinterpret_cast<T*>(buffer);
auto obj1 = t[0]; // UB: T does not alias std::byte; (strict aliasing);
问题:
- 标准中的哪一条规定会让上述代码成为定义行为?
- 否则,像
std::vector-那样的容器如何让这些问题变得正确?
解决方案
感谢所有的评论和回答,我认为 @Artyer的方向是正确的,但如果元素类型不是const,则不需要std::launder()。
下面就一些可能的疑问来回答:
- Object lifetime and Providing storage 使相关底层字节的生命周期在对其对应地址使用placement new(T) 时隐式结束。
- reinterpret_cast保证
T * -> char * -> T *回到原始值(例如由placement new(T) 返回的值)。(https://en.cppreference.com/cpp/language/reinterpret_cast#Explanation) (list 5) - T对自身是 type-accessible 的。
并且,
- std::launder() 解决的问题与此不同;(Map
> 中的const应如何处理?) - start_lifetimes_as() 适用于平凡类型,如聚合类型,而不是用于placement new和一般情况;
- 严格别名规则不适用,因为在使用placement new之后,该地址上已经开始了一个活动对象的生命周期,因此把它的地址转换为字节指针再转换回来并不会产生UB。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。