对于非空初始化,生存期在初始化之前就开始了,它解决了什么问题?

时间:2018-09-04 10:34:38

标签: c++ c++17

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

  

对象或引用的生存期是对象或引用的运行时属性。如果对象属于类或聚合类型,并且其或其子对象之一由普通的默认构造函数以外的构造函数初始化,则称该对象具有非空初始化。 [注意:琐碎的复制/移动构造函数的初始化是 non-vacuous 初始化。 — end note]类型T的对象的生存期始于以下时间:

     

(1.1)获得类型T正确的对齐方式和大小的存储,并且

     

(1.2)如果对象具有非空初始化,则其初始化已完成

为什么初始化在非空的中很重要?换句话说,如果初始化不是 non-vacuous ,为什么寿命在初始化完成之前开始?它解决什么问题?

如果(1.2)读为

,将会有什么后果?
  

(1.2)如果对象已初始化,则其初始化完成

术语非空的仅在此处使用,它必须具有一定的原因。


请注意,我的here之前也有类似的问题,但是由于这个问题有点太广泛了(甚至我接受了其中一个答案),因此那里的回答也无法回答这个当前问题。

3 个答案:

答案 0 :(得分:3)

我认为这是为了解决不执行初始化的默认初始化:

  

[dlc.init]:

     

要默认初始化T类型的对象,意味着:

     

(7.1)—如果T是(可能是cv限定的)类类型(第12条),则考虑构造函数。列举了适用的构造函数(16.3.1.3),并通过重载解析(16.3)选择了最佳的初始化方法()。如此选择的构造函数将被调用,并使用一个空的参数列表来初始化对象。

     

(7.2)—如果T是数组类型,则每个元素都将默认初始化。

     

(7.3)-否则,不执行初始化。

我知道所有对象都有初始化,但是对于某些具有默认初始化的对象,不执行初始化。请注意,它专门指出“未执行初始化”,而不是它不存在。

您可以具有一个未初始化的默认初始化对象,因此无法完成。他们不会开始他们的生活,并且以当前的措词,他们不必等待这种没有初始化的事情发生。另外,此类对象还必须进行初始化,因为它是默认初始化的。

恕我直言,您的措辞可能有缺陷,或者您的理解会导致7.3的默认初始化定义的逻辑继承,因为此类对象可能会:

  1. 已初始化,并且由于未执行而无法开始使用。
  2. 进行默认初始化,但不进行初始化。我知道这可能会造成混淆。

使用当前的措辞很清楚。也许某个地方可以明确地说出哪些对象已初始化或进行初始化意味着什么。我想一切都可以,但我手拿不到。

答案 1 :(得分:1)

涉及“非空初始化”定义的标准引号与不带“非空初始化”的备用句子之间的区别是,标准句子的确允许某些情况下对象的生存期在其初始化完成之前开始。

有三种方法(我可以想到),在初始化完成之前可以使用一个对象:

  1. 对象是在名称空间范围内定义的或作为静态类数据成员定义的,因此具有静态存储持续时间,并且几乎可以在任何范围内使用其名称。该对象还具有动态初始化。

  2. 对象的名称在其自己的初始化程序中使用。

  3. 可以通过构造函数内的this(可能是隐式的)访问对象。

#2并不常见,在这里总是将#3排除为非空初始化,因此让我们看一下#1。 #1是静态初始化顺序惨败的确切设置,但是通过“不空洞”的措辞,标准称这些情况实际上还可以:

int f();
int n = f();
int a[3] = {f(), f(), f()};
struct X {};
struct Y { int p; int q[2]; X x; };
Y y = { f(), { f(), f() }, {} };

这里所有声明的对象都具有零初始化和动态初始化。它们可以在另一个对象的动态初始化期间使用,该对象在相同的转换单元中(或在其自己的初始化程序中)在较早时定义的静态存储持续时间中使用,因为它们的生命周期已经开始,并且所有int对象的值将为零。它们可以在不同转换单元中定义的对象的动态初始化期间使用,其行为是未指定的,而不是未定义的,int值为零或适当的f()表达式的返回值

答案 2 :(得分:0)

例如

FirebaseApp.initializeApp(this);
FirebaseFirestore rootRef = FirebaseFirestore.getInstance()

对象int i = i; 的生存期是在评估初始化器i时开始的,因此这行代码是明确定义的(尽管i将具有不确定的值,并且未定义为使用该值。


另一个有意义的示例:

i

这是定义明确的。没有“非空缺”的限制,这将是不确定的,因为struct A { int i, j; int square() { return i * i; } }; A a{0, a.square()}; 用于在a的生命周期开始之前调用非静态成员函数。