通过 placement-new 手动构建一个平凡的基础 class

Manually constructing a trivial base class via placement-new

小心,我们正在绕过巨龙的巢穴。

考虑以下两个 类:

struct Base {
    std::string const *str;
};

struct Foo : Base {
    Foo() { std::cout << *str << "\n"; }
};

如您所见,我正在访问一个未初始化的指针。还是我?

假设我只使用 Basetrivial,只不过是(可能嵌套的)指针包。

static_assert(std::is_trivial<Base>{}, "!");

我想分三步构建Foo

  1. Foo

  2. 分配原始存储
  3. 通过 placement-new

  4. 初始化一个适当放置的 Base 子对象
  5. 通过 placement-new 构造 Foo

我的实现如下:

std::unique_ptr<Foo> makeFooWithBase(std::string const &str) {

    static_assert(std::is_trivial<Base>{}, "!");

    // (1)
    auto storage = std::make_unique<
        std::aligned_storage_t<sizeof(Foo), alignof(Foo)>
    >();

    Foo * const object = reinterpret_cast<Foo *>(storage.get());
    Base * const base = object;

    // (2)
    new (base) Base{&str};

    // (3)
    new (object) Foo(); 

    storage.release();
    return std::unique_ptr<Foo>{object};
}

由于Base是微不足道的,我的理解是:

因此 Foo 收到一个初始化指针,一切正常。

当然,这就是实践中发生的情况,即使在 -O3 (see for yourself!)。
但是这样安全吗,要不哪天被龙抢食了?

标准似乎明确禁止。 结束一个对象的生命周期,并开始一个新的对象 明确允许在同一位置的生命周期, 除非它是一个基础class:

§3.8 Object Lifetime

§3.8.7 - 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

  • [snip] and

  • the original object was a most derived object (1.8) of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).