具有空洞初始化的对象的生命周期

Lifetime of object which has vacuous initialization

当前标准草案在[basic.life/1]中说(以前的标准有类似的措辞):

The lifetime of an object or reference is a runtime property of the object or reference. 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. [ Note: Initialization by a trivial copy/move constructor is non-vacuous initialization. — end note ] The lifetime of an object of type T begins when:

(1.1) storage with the proper alignment and size for type T is obtained, and

(1.2) if the object has non-vacuous initialization, its initialization is complete,

查看这段代码:

alignas(int) char obj[sizeof(int)];

basic.life/1 是否意味着这里 int(以及其他几种类型,与 int 具有相同或更少的 alignment/size 要求)已经开始寿命?

这到底是什么意思?如果一个对象已经开始它的生命周期,它是否被创建? [intro.object/1] 说:

[...] An object is created by a definition ([basic.def]), by a new-expression, when implicitly changing the active member of a union ([class.union]), or when a temporary object is created ([conv.rval], [class.temporary]) [...]

因此,据此,我的 obj(作为 int)未创建。但是它作为 int(以及其他可能是无限类型的可空初始化对象)的生命周期已经开始。

我很困惑,你能解释一下吗?

我解读

The lifetime of an object of type T begins when...

意思是

Given that a program creates an object of T, the following describes when that object's lifetime is said to begin...

而不是

If the following conditions are satisfied, then an object of type T exists, and its lifetime begins when...

也就是说,有一个隐含的附加条件,即对象在 [intro.object]/1 中以某种方式 "created"。但是段落 [basic.life]1/ 本身并不意味着任何对象存在,只是存在的对象的一个​​属性。

因此对于您的声明,文本描述了一个 char[sizeof(int)] 类型的对象和一个或多个 char 类型的对象的生命周期的开始(即使声明是一个声明块作用域并且没有初始化),但是由于没有暗示存在类型 int 的对象,我们不会说任何关于此类对象的生命周期的信息。

除非对象已经创建,否则您无法开始对象的生命周期。 [intro.object]/1 定义了创建对象的唯一方式:

An object is created by a definition (6.1), by a new-expression (8.3.4), when implicitly changing the active member of a union (12.3), or when a temporary object is created (7.4, 15.2).

该定义创建的对象类型为char[]。因此,这是生命周期开始的唯一对象。此构造不会创建其他对象。

为了证实这种解释,存在 C++20 P0593 提案,其主要目的是 允许 隐式创建其他此类对象的声明.


评论:

The condition in (1.2) still bothers me. Why is it there?

它在那里是因为它不能为不经过初始化的对象说 "the initialization is complete"。

suppose, that I have a new(obj) int afterwards. That clearly creates an int object. But before that, obj has obtained the necessary storage.

不,obj 的声明获得了 char[] 类型对象的存储空间。为正在创建的 int 对象获取存储的是 new(obj)。是的,placement-new 表达式为其创建的对象获取存储空间。就像变量的声明为其创建的对象获取存储一样。

仅仅因为该存储恰好已经存在并不意味着它没有被获取。

由于标准有意避免要求所有实现都适用于所有目的,因此通常有必要针对用于各种目的的质量实现来保证代码的行为,而标准本身不会对其施加任何要求。

如果某些类型 T 支持隐式对象创建并且程序将某些对象的地址转换为 T*,则高质量的实现旨在支持低级编程概念而无需需要特殊语法的行为就好像这种转换会创建一个 T 类型的对象,在这种情况下允许程序具有已定义的行为,但不会隐式创建此类对象,在这种情况下这样做不是必需的,但会而是通过破坏其他对象导致未定义的行为。

因此,如果 floatuint32_t 大小相同并且具有相同的对齐要求,则给出例如

alignas(uint32_t) char obj[sizeof(uint32_t)];

float *fp = (float*)obj;
*fp = 1.0f;
uint32_t *up = (uint32_t*)obj;

fp 的初始化将创建一个 float,因为需要它才能使 *fp 的分配工作。如果 up 将以需要 uint32_t 存在的方式使用,则对 up 的赋值可能会创建一个,同时破坏那里的 float。如果 up 没有以这种方式使用,但是 fp 的使用方式要求 float 仍然存在,那么 float 仍然存在。如果两个指针的使用方式要求各自的对象仍然存在,那么即使是用于低级编程的高质量编译器也可能无法处理这种可能性。

请注意,不是特别适合低级编程的实现可能不支持此处描述的语义。该标准的作者允许编译器编写者支持或不支持此类语义,具体取决于它们是否是编译器预期目的所必需的;不幸的是,目前还没有任何标准方法来区分适合此类目的的编译器和不适合此类目的的编译器。