使用新放置来更新参照杆件

Using placement new to update a reference member?

本文关键字:更新 新放      更新时间:2023-10-16

以下代码在C++中合法吗?

template<typename T>
class Foo {
public:
    Foo(T& v) : v_(v) {}
private:
    T& v_;
};
int a = 10;
Foo<int> f(a);
void Bar(int& a) {
    new (&f)Foo<int>(a);
}

引用不应该绑定两次,对吧?

这是完全无效的。

[基本生活]/1,重点矿:

类型为T的对象的生存期在以下情况下结束:

  • 如果T是具有非平凡析构函数(12.4)的类类型,则析构函数调用启动,或者
  • 对象占用的存储被重用或释放

放置new重用存储,结束由f表示的对象的生命周期。

[基本生活]/7:

如果,在对象的生存期结束之后,在存储之前被占用的对象被重新使用或释放,新对象在原始对象占用的存储位置创建指向原始对象的指针,引用到原始对象,或者原始对象的名称将自动引用新对象,并且在新对象已经启动,可以用于操作新对象,如果:

  • 新对象的存储正好覆盖原始对象所占用的存储位置,以及
  • 新对象与原始对象的类型相同(忽略顶级cv限定符),并且
  • 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为的非静态数据成员const限定或引用类型,以及
  • 原始对象是类型为T的派生次数最多的对象(1.8),而新对象是类型T的派生次数最少的对象(也就是说,它们是而不是基类子对象)

由于不满足第三个要点,在调用Bar之后,f不是指由放置new创建的对象,而是指之前不再存在的对象,并且尝试使用它会导致未定义的行为。

另请参见CWG1776和P0137R0。

它可能是合法的,但它的风格非常糟糕。放置new的参数是一个void*,所以你告诉C++将f的地址重新解释为void*,然后用它作为位置来构建新的东西-覆盖原始的f。

基本上,不要那样做。