对于普通对象,在 `this` 上调用 placement new 是否安全?

Is it safe to call placement new on `this` for trivial object?

我知道这个问题已经被问过好几次了,但我找不到这个特定案例的答案。

假设我有一个微不足道的 class,它不拥有任何资源并且具有空的析构函数和默认构造函数。它有少量的成员变量带有 in-class 初始化;没有一个是 const.

我想重新初始化和对象这样的 class 它而不用手动编写 deInit 方法。这样做安全吗?

void A::deInit()
{
  new (this)A{};
}

我看不出有什么问题 - 对象始终处于有效状态,this 仍然指向相同的地址;但它是 C++,所以我想确定一下。

delete this 的合法性类似,据我所知,this 的新位置也是允许的。此外,关于 this 或其他预先存在的指针/引用是否可以在之后使用,有一些限制:

[basic.life]

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied, and
  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
  • neither the original object nor the new object is a potentially-overlapping subobject ([intro.object]).

前两个在本例中已经满足,但需要考虑后两个。

关于第三点,鉴于函数是非 const 限定的,假设原始对象是非 const 应该是相当安全的。如果常量已被丢弃,则错误在调用方。关于 const / reference 成员,我认为可以通过断言这是可分配的来检查:

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

当然,由于可分配性是一项要求,您可以改为简单地使用 *this = {};,我希望它能生成相同的程序。一个可能更有趣的用例可能是为另一种类型的对象重用 *this 的内存(这将不符合使用 this 的要求,至少没有重新解释 + 洗钱)。

delete this 类似,this 的新位置很难描述为 "safe"。

涵盖此内容的规则在 [basic.life]/5

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type. For an object of a class type, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression is not used to release the storage, the destructor is not implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.

[basic.life]/8

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied, and

  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and

  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and

  • neither the original object nor the new object is a potentially-overlapping subobject ([intro.object]).

由于您的对象是微不足道的,您不必担心 [basic.life]/5,只要您满足 [basic.life]/8 中的要点,那么它就是安全。