std::launder必须在placement new之后、首次访问前调用,否则高优化下可能读旧值或被删除;它只接受对象起始地址,偏移或未对齐指针导致UB;union切换活跃成员后通过非构造返回指针访问也需launder。

std::launder必须在placement new之后、首次访问前调用
不加 std::launder 就解引用,高优化级别下(-O2 或更高)可能读到旧值、跳过实际内存访问,甚至被编译器整个删掉——这不是 bug,是标准允许的未定义行为。
典型错误写法:
std::byte buffer[sizeof(MyClass)];
MyClass* p = new (buffer) MyClass{42};
int x = p->value; // ❌ UB:p 未经过 launder,编译器不承认它指向一个活跃的 MyClass 对象
正确顺序必须是:
- 分配原始内存(
operator new、std::aligned_storage_t、std::byte[]等) - 用
placement new构造对象,拿到返回指针(可选保存) - 立即对**该指针**或**精确对齐的原始地址**调用
std::launder - 再进行任何成员访问、函数调用或取址操作
std::launder只接受指向对象起始地址的指针
传入偏移后的地址(比如 &buffer[1])或未对齐指针,行为是未定义的。它不校验对齐,但要求输入地址与对象实际起始地址完全一致。
立即学习“C++免费学习笔记(深入)”;
常见误用场景:
- 从
char*缓冲区构造对象后,用reinterpret_cast<t>(buf + offset)</t>再调std::launder—— 若offset != 0,直接 UB - 对
std::launder(p)->member的结果再取地址并 launder,试图“链式唤醒”——没意义,std::launder只作用于对象本身起始地址 - 对数组首元素指针调用
std::launder后,用p + i访问其他元素 —— 合法,但std::launder本身只认证了p所指对象,不扩展到整个数组
安全做法始终是:构造完成后,用 std::launder(static_cast<t>(raw_ptr))</t>,且确保 raw_ptr 恰好等于对象起始地址。
union 切换活跃成员后访问新成员必须 launder
C++17 要求 union 中显式构造新成员后,若通过 union 外部指针(非 placement new 返回值)访问,必须 std::launder。否则即使类型兼容,也可能因别名分析失效导致读错字节。
例如:
union U {
int i;
float f;
};
U u;
u.i = 42;
// 切换活跃成员
new (&u.f) float{3.14f}; // f 成为活跃成员
float* pf = &u.f; // ✅ 这个指针合法,无需 launder
float* raw = reinterpret_cast<float*>(&u); // ❌ 原始地址指针,未认证
std::cout << *std::launder(raw); // ✅ 安全访问 f
注意:&u.f 是 placement new 的直接结果,自带生命周期认证;而 reinterpret_cast<float>(&u)</float> 是泛型地址转换,不带语义,必须显式 std::launder。
std::launder不是类型转换,也不能绕过 const/volatile
它不改变指针类型、不解除 const、不参与严格别名规则的绕过。你不能靠它把 const int* 变成可修改的指针,也不能用它让 char* 安全读写 int 字段。
容易混淆的边界:
-
std::launder(const T*)返回仍是const T*,无法用于赋值 - 想改对象?得先有非 const 指针,再
std::launder,否则编译不过 - 用
reinterpret_cast<int></int>读char[]内容?std::launder不解决 strict aliasing,该 UB 还是 UB - 它只回答一个问题:“这个地址上,现在是否有一个类型为 T 的活跃对象?”——仅此而已
最常被忽略的一点:launder 不是“一次调用、永久有效”。它只建立该指针的首次合法访问路径;后续所有访问都依赖这个已认证的指针,而不是反复调用 std::launder。


















