在具有引用成员的对象上使用放置“new”的结果
Using placement `new`'s result on a object with a reference member
问题显示了这个例子(简化):
struct Foo { int& v_; };
int a, b;
Foo f{a};
new (&f) Foo{b};
assert(&f.v_ == &a); // UB
通过原名访问f
肯定是UB as 。我知道 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] 中有很多关于如何使用 old 名称可以和不可以的规则- 有关于如何使用 f
、指向 f
的先前指针等的规则。如果没有 launder
-ing,则不能重用 &f
。在这种情况下,有关于如何以及何时调用析构函数的规则,或者如何为静态存储或 const 对象重用存储 - none 在这里都很重要。
但是p
是一个新事物——它只是指代新对象。 p->v_
是您创建的引用 b
的新 int&
。这与 a
不是同一个对象,因此指针比较不相等。
struct Foo { int& v_; };
int a, b;
Foo f{a};
new (&f) Foo{b};
assert(&f.v_ == &a); // UB
通过原名访问f
肯定是UB as 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] 中有很多关于如何使用 old 名称可以和不可以的规则- 有关于如何使用 f
、指向 f
的先前指针等的规则。如果没有 launder
-ing,则不能重用 &f
。在这种情况下,有关于如何以及何时调用析构函数的规则,或者如何为静态存储或 const 对象重用存储 - none 在这里都很重要。
但是p
是一个新事物——它只是指代新对象。 p->v_
是您创建的引用 b
的新 int&
。这与 a
不是同一个对象,因此指针比较不相等。