在const对象自身位置重建对象:返回值与UB问题探讨
const对象销毁后重建的行为与std::launder使用解析
在以下场景中:调用析构函数结束const对象s的生命周期,随后在同一内存位置创建同类型S的新对象。由于违反了[basic.life]/8.3规则(针对const对象的限制),访问新创建的S对象必须使用std::launder。
#include <new> #include <utility> struct S { int x; }; template <class T, class... Pars> void Recreate(const T& value, Pars&&... pars) { value.~T(); new(const_cast<T*>(&value)) T(std::forward<Pars>(pars)...); } int main() { const S s(1); Recreate(s, 2); return std::launder(&s)->x; }
问题解答
程序的返回值与是否存在未定义行为
这个程序没有未定义行为,且必须返回2。
当Recreate(s, 2)执行时,原const对象s被销毁,同一块内存上重建了值为2的新S对象。由于原对象是const限定类型,根据C++标准[basic.life]/8.3的规则,原对象的名字s无法自动关联到新对象,此时必须通过std::launder来获取指向新对象的合法指针。示例中std::launder(&s)->x的写法是合规的,std::launder调整指针后,程序可以合法访问新对象的成员x,因此返回值必然是2。std::launder的正确使用方式
- 适用场景:当在已结束生命周期的对象内存位置上重建同类型(或兼容类型)对象,且原对象满足以下任一条件时,必须使用
std::launder:- 原对象是const限定类型
- 原对象包含非静态const成员
- 原对象是引用类型
- 使用方法:将指向原对象内存地址的指针传入
std::launder,它会返回指向新对象的有效指针。比如示例中的std::launder(&s),即可获取指向新创建S对象的合法指针。 - 注意事项:不要滥用
std::launder,只有符合[basic.life]规定的场景才需要使用,多余的调用不会带来收益,还可能干扰编译器优化。
- 该场景的规范是否存在歧义
C++标准中[basic.life]的相关规则是明确的:const对象的名字在其生命周期结束后,无法自动指代同内存位置上的新对象,必须通过std::launder来建立新对象的合法访问路径。Note 3仅为辅助说明,规范本身的要求清晰,不存在歧义。
内容的提问来源于stack exchange,提问作者Dr. Gut
相关产品推荐
相关产品推荐

