在自己的初始化程序中使用变量

wal*_*nut 23 c++ initialization lifetime language-lawyer c++20

C++20 标准草案的[basic.scope.pdecl]/1在注释中包含以下(非规范性)示例(合并拉取请求 3580之前的部分引用,请参阅此问题的答案):

unsigned char x = x;
Run Code Online (Sandbox Code Playgroud)

[...] x 用它自己的(不确定的)值初始化。

这实际上在 C++20 中有明确定义的行为吗?


通常T x = x;,由于x的值在初始化完成之前是不确定的,因此表单的自初始化具有未定义的行为。评估不确定的值通常会导致未定义的行为([basic.indent]/2),但在[basic.indent]/2.3中有一个特定的例外,它允许直接unsigned charunsigned char具有不确定值的左值初始化变量(导致使用不确定值初始化)。

这本身并不会因此导致不确定的行为,但会为其他类型T不属于无符号窄字符类型或std::byteint x = x;。这些注意事项也适用于 C++17 及之前的版本,另请参阅底部的链接问题。

然而,即使对于unsigned char x = x;,当前草案的[basic.lifetime]/7说:

类似地,在对象的生命周期开始之前 [...] 使用不依赖于其值的泛左值的属性是明确定义的。在以下情况下,程序具有未定义的行为:

  • 泛左值用于访问对象,或

  • [...]

这似乎意味着x示例中的that值只能在其生命周期内使用。

[basic.lifetime]/1说:

[...]

类型 T 的对象的生命周期在以下情况下开始:

  • [...] 和
  • 它的初始化(如果有)完成(包括空初始化)([dcl.init]),

[...]

因此x的生命周期仅在初始化完成后才开始。但是在引用的例子中x的值是在x初始化完成之前使用的。因此,使用具有未定义的行为。

我的分析是否正确,如果正确,是否会影响类似的使用前初始化的情况,例如

int x = (x = 1);
Run Code Online (Sandbox Code Playgroud)

据我所知,在 C++17 及之前的版本中,哪些是明确定义的?


请注意,在 C++17(最终草案)中,生命周期开始的第二个要求是不同的

  • 如果对象有非空初始化,则其初始化完成,

由于xC++17 的定义(但不是当前草案中的定义)会进行空初始化,因此在上面给出的示例中的初始化程序中访问它时,它的生命周期已经开始,因此在两个示例中都没有未定义的行为由于x在 C++17 中的生命周期。

C++17 之前的措辞再次不同,但结果相同。


问题不在于使用不确定值时的未定义行为,例如以下问题中的内容:

Nic*_*las 9

这是作为编辑问题打开的。它已转发给 CWG 进行(内部)讨论。大约 24 小时后,转发问题的人创建了一个拉取请求,该请求修改了示例以明确这是 UB:

在这里,第二个 \tcode{x} 的初始化具有未定义的行为,因为初始化程序在其生命周期 \iref{basic.life} 之外访问第二个 \tcode{x}。

此后已添加该 PR,问题已关闭。因此,显而易见的解释(UB 由于访问生命周期尚未开始的对象)是预期的解释。委员会的意图似乎使这些结构不起作用,并且标准的非规范性文本已更新以反映这一点。