必须在placement new重构造对象后、用原指针访问新对象时使用std::launder,否则导致未定义行为;它告知编译器该地址已有新活跃对象,使指针访问合法。

std::launder 什么时候必须用?
当你在一块已分配的内存里用 placement new 重新构造对象,又想通过原有指针访问新对象时,std::launder 就不是可选的——它是必需的。否则行为未定义(UB),尤其在优化级别较高(如 -O2)时,编译器可能直接删掉你的读取或返回错误值。
典型场景:对象池、variant 内部实现、手动管理生命周期的容器(比如 std::optional 的底层)。不是所有重构造都需要它,只有“同一块内存、不同对象生命周期”且你持有旧指针时才触发这个需求。
不加 std::launder 会出什么错?
最常见的是读到陈旧值,甚至崩溃。例如:
struct S { int x; S(int v) : x(v) {} };
alignas(S) char buf[sizeof(S)];
S* p = new (buf) S(42);
// 此时 p 指向有效对象
p->x = 100;
new (p) S(200); // 重构造
// ❌ 错误:直接用 p 访问新对象
int bad = p->x; // 可能仍是 100,或触发 UB
原因:编译器认为 p 关联的是第一个 S 对象,其生命周期已结束;后续对 p->x 的访问不被视为指向新对象的有效访问路径。
-
std::launder(p)告诉编译器:“请重新检查这块内存,现在它有新对象了” - 没有它,即使
p地址没变,也不能安全用于访问新对象 - Clang 和 GCC 在
-O2下对此非常激进,容易暴露问题
std::launder 的参数和返回值怎么用?
std::launder 接收一个指向对象起始地址的指针(T*),返回类型相同。关键约束:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 传入指针必须指向对象的**起始地址**(不能是成员指针、偏移后地址)
- 该内存区域当前必须包含一个活跃的
T对象(即重构造已完成) - 返回指针才能用于访问新对象;原指针仍不可用
正确写法:
S* p = new (buf) S(42); new (p) S(200); S* q = std::launder(p); // ✅ 必须用这个指针 int good = q->x; // 安全,得到 200
注意:std::launder 不改变地址值,也不做运行时检查;它只是给编译器加一道“访问许可”。传入非法地址(比如 nullptr 或未构造内存)仍是 UB。
哪些情况其实不需要 std::launder?
容易误用的地方很多,但以下情况完全不需要:
- 用
new或变量定义创建的对象(生命周期自然管理) - 重构造后立即用新
placement new返回的指针(如S* q = new (p) S(200);) - 对象类型无非静态数据成员、无虚函数、平凡可复制(trivial)——但即使如此,只要用旧指针访问,仍需
std::launder
一个反直觉点:即使 sizeof(S) == 1 且无成员,只要你在同一地址上结束旧对象、开始新对象,并打算用旧指针访问,就必须 std::launder。标准不看内容是否“简单”,只看对象生命周期是否切换。
真正容易被忽略的是:它只解决“指针有效性”的语义问题,不解决内存对齐、析构调用、异常安全等其他问题。重构造前必须显式调用旧对象的析构函数(如果非 trivial),否则也是 UB —— std::launder 不替你做这件事。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










