由 placement-new 创建的普通类型的生命周期从什么时候开始?

At what point does the lifetime of a trivial type created by placement-new start?

在深入研究动态内存的过程中,我发现琐碎的类型如何开始其生命周期似乎是矛盾的。考虑片段

void* p = ::operator new(sizeof(int));  // 1
// 2
new (p) int; // 3

int 什么时候开始它的生命周期?

  1. 只获取存储,::operator new指定有效(来自[new.delete.single]

    The allocation functions called by a new-expression to allocate size bytes of storage. [...] allocates storage suitably aligned to represent any object of that size provided the object's type does not have new-extended alignment.

    鉴于在创建对象时获取存储 int 不可能从这里开始其生命周期。

  2. 此时,已获得 int 的套装存储空间。

  3. int 是由放置新创建的。但不知何故,它的生命周期并不是从这里开始的,因为从 [basic.life]

    [...] An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. The lifetime of an object of type T begins when:

    • storage with the proper alignment and size for type T is obtained, and

    • if the object has non-vacuous initialization, its initialization is complete [...]

    int 既不是 class 也不是聚合类型,因此它具有空洞的初始化。因此,只有第一个项目符号适用。但是,这显然不是获得存储的时间,因此不可能是其生命周期开始的时间。

一些背景

分配器 are required 到 return 内存而不构造其元素。然而,这对于普通类型没有意义。 a.allocate(n)a 类型 T 的分配器对象的效果是

Memory is allocated for n objects of type T but objects are not constructed.

从技术上讲,new-expression 总是获得存储空间。代码new(p) int既获取存储又创建对象,根据[basic.life]/1,对象的生命周期从new(p) int获取存储开始。

根据 N4659 [expr.new],代码 new(p) int 生成对分配函数 ::operator new(sizeof(int), p) 的调用。而在[new.delete.placement]下,标准库定义了这样一个函数:

void* operator new(std::size_t size, void* ptr) noexcept;

Returns: ptr.

Remarks: Intentionally performs no other action.

虽然执行了 "no other action",并且可能实现会优化任何实际的函数调用,但这种对分配函数的调用仍然算作为 [ 创建的对象获取存储空间=27=].