哪种场景下必须使用std::launder以避免C++未定义行为?
C++20中placement new后是否需要
std::launder的场景分析 先看示例代码:
#include <iostream> struct NonImplicitLifetime{ NonImplicitLifetime(){} ~NonImplicitLifetime(){} }; int main(){ // case 1: auto ptr = (NonImplicitLifetime*) malloc(sizeof(NonImplicitLifetime)*2); // #1 auto second = ptr+1; new (second) NonImplicitLifetime{}; *second; // #2 // case 2: unsigned char buff[sizeof(NonImplicitLifetime) * 2]; // #3 auto ptr_second = buff + sizeof(NonImplicitLifetime); new (ptr_second) NonImplicitLifetime{}; *(NonImplicitLifetime*)ptr_second; // #4 }
Case 1 分析
在C++20中,malloc分配的内存会隐式创建包含2个NonImplicitLifetime对象的数组,second指向该数组的第二个元素。当通过placement new在该位置创建新对象时,完全符合[intro.object]第2款的规则:
如果在与成员子对象或数组元素e(可能处于生命周期内,也可能不处于)关联的存储中创建对象,当满足以下条件时,新创建的对象是e所属对象的子对象:
- e所属对象的生命周期已开始且未结束,
- 新对象的存储与e关联的存储位置完全重叠,
- 新对象与e的类型相同(忽略cv限定符)。
此时新对象是原数组的子对象,second会自动指向新创建的对象,不需要使用std::launder,#2处的解引用操作是合法的。
Case 2 分析
unsigned char buff[]是unsigned char类型的数组,其元素类型与NonImplicitLifetime完全不同。当在ptr_second指向的存储位置(对应原数组的某个unsigned char元素)创建NonImplicitLifetime对象时,不满足[intro.object]第2款的“类型相同”条件,因此新对象不是原数组的子对象——原数组仅提供存储空间(依据[intro.object]第3款)。
此时(NonImplicitLifetime*)ptr_second这个指针仍然关联着生命周期已结束的unsigned char元素,而非新创建的NonImplicitLifetime对象。直接解引用这个指针属于未定义行为,必须使用std::launder来获取指向新对象的有效指针,修正指针的类型关联后才能合法访问。
内容的提问来源于stack exchange,提问作者xmh0511
相关产品推荐
相关产品推荐

