std::launder的作用效果是否会在其调用的表达式结束后持续生效?
示例代码
struct X { const int n; }; union U { X x; float f; }; void fun() { U u = {{ 1 }}; u.f = 5.f; // OK, creates new subobject of 'u' X *p = new (&u.x) X {2}; // OK, creates new subobject of 'u' if(*std::launder(&u.x.n) == 2){// condition is true because of std::launder std::cout << u.x.n << std::endl; //UB here? } }
结论
这段代码中std::cout << u.x.n << std::endl;属于未定义行为,程序没有确定的输出结果。
具体解释
- std::launder的生效范围仅局限于它所在的表达式,不会对后续其他表达式的访问产生持续效果。它的核心作用是返回一个指向新创建对象的合法指针/左值,仅对当前返回值对应的访问有效,不会修改原有变量(比如本例中的
u.x、u.x.n)的访问合法性。 - 由于X结构体包含const非静态成员
n,你通过placement new在&u.x位置创建新的X对象后,原有的变量名u.x不能直接用来指代这个新创建的对象,符合C++标准中需要显式调用std::launder才能合法访问新对象的场景。你在if条件中调用std::launder(&u.x.n)获得了指向新对象n成员的合法指针,所以判断条件结果确实为true,但这一调用不会让后续直接访问u.x.n的操作合法化,因此cout行存在未定义行为。 - 如果不想每次访问都调用
std::launder,你可以保存它返回的合法指针复用,比如:
只要你持有这个经过launder的合法指针,后续通过该指针的访问都不需要重复调用X* valid_x = std::launder(&u.x); if (valid_x->n == 2) { std::cout << valid_x->n << std::endl; // 合法操作,无未定义行为 }std::launder。但如果直接使用原变量名u.x访问,每次都需要调用std::launder才合法。
内容的提问来源于stack exchange,提问作者John Z. Li
相关产品推荐
相关产品推荐

