C++14中Placement new结合reinterpret_cast是否合规?
C++14对齐存储对象访问的合法性解析
原代码的合法性与严格别名规则判断
先看你给出的C++14代码:
alignas(T) unsigned char data[sizeof(T)]; new (data) T(); T* p = reinterpret_cast<T*>(data); p->something(); // UB?
这段代码在C++14中完全合法,不存在违反严格别名规则的问题。
标准依据
C++14标准的[basic.lval]条款明确了严格别名规则的例外情况:
- 允许用
char或unsigned char类型的glvalue访问任何对象的底层字节;反之,当你通过placement new在unsigned char数组的存储位置上构造了一个T类型的对象后,该存储位置的实际对象类型已变为T,此时用reinterpret_cast将unsigned char*转换为T*并访问这个T对象,完全符合标准要求——因为你是通过与对象实际类型匹配的glvalue来访问它的。
C17的std::launder要求对C14的影响
再看cppreference上的示例代码:
// Access an object in aligned storage const T& operator[](std::size_t pos) const { // Note: std::launder is needed after the change of object model in P0137R1 return *std::launder(reinterpret_cast<const T*>(&data[pos])); }
C++17通过P0137R1修订了对象模型,引入了更严格的“对象身份”规则:即使你在unsigned char数组的存储位置上构造了新的T对象,编译器可能仍会认为该位置的对象还是原来的unsigned char数组,直接用reinterpret_cast得到的T*访问可能被编译器优化(视为无效指针),因此必须用std::launder告知编译器该指针指向的是一个新构造的对象,需要重新识别其身份。
但在C++14中,并没有这样的对象模型修改。编译器会承认在unsigned char数组存储位置上构造的T对象的身份,因此不需要std::launder,直接转换后访问是安全的,不会触发未定义行为。
内容的提问来源于stack exchange,提问作者user1011113
相关产品推荐
相关产品推荐

