"Using placement new to update a reference member?"问题显示了这个例子(简化):
struct Foo { int& v_; };
int a, b;
Foo f{a};
new (&f) Foo{b};
assert(&f.v_ == &a); // UB
通过其原始名称访问f
肯定是UB作为explained by T.C. in the linked question。我知道std::launder
可以用来解决这个问题:
assert(&std::launder(&f)->v_ == &a); // OK, will fire the assert
但是使用放置new
返回的指针怎么样?即
auto p = new (&f) Foo{b};
assert(&(p->v_) == &a); // UB? OK?
在这种情况下,我们不是通过其原始名称引用该对象,而是通过new
返回的任何位置。
这是未定义的行为还是标准所允许的?
这个:
auto p = new (&f) Foo{b};
assert(&(p->v_) == &a); // UB? OK?
定义明确。断言将触发。 new
创建了一个新对象,p
指向该新对象。这一切都很好。我们正在重复使用f
的存储,[basic.life]中有很多关于什么是好的规则而不是关于如何使用旧名称的规则 - 有关于如何使用f
,以前指向f
等的规则。你不能重复&f
没有launder
- 它。有关在这种情况下如何以及何时可以调用析构函数的规则,或者如何重用存储用于静态存储或const对象 - 这些都不重要。
但p
是一个新东西 - 它只是指新的对象。而p->v_
是你创造的新的int&
,指的是b
。这与a
不是同一个对象,所以指针比较不相等。