【发布时间】:2020-04-13 23:41:05
【问题描述】:
C++20 标准草案的[basic.scope.pdecl]/1 在注释中有以下(非规范)示例(pull request 3580 合并之前的部分引用,请参阅此问题的答案):
unsigned char x = x;
[...] x 用它自己的(不确定的)值初始化。
这在 C++20 中真的有明确定义的行为吗?
由于x 的值在初始化完成之前不确定,通常T x = x; 表单的自初始化具有未定义的行为。评估不确定的值通常会导致未定义的行为 ([basic.indent]/2),但 [basic.indent]/2.3 中有一个特定的例外,它允许从具有不确定值的左值 unsigned char 直接初始化 unsigned char 变量(导致使用不确定值进行初始化)。
因此,仅此一项不会导致未定义的行为,但会导致其他类型 T 不是无符号窄字符类型或 std::byte,例如int x = x;。这些注意事项也适用于 C++17 及之前的版本,另请参阅底部的链接问题。
然而,即使对于unsigned char x = x;,当前草案的[basic.lifetime]/7 表示:
类似地,在对象的生命周期开始之前 [...] 使用不依赖于其值的泛左值的属性是明确定义的。如果出现以下情况,则程序具有未定义的行为:
glvalue 用于访问对象,或
[...]
这似乎暗示了示例中x的值只能在其生命周期内使用。
[...]
类型 T 的对象的生命周期开始于:
- [...] 和
- 其初始化(如果有)已完成(包括空初始化)([dcl.init]),
[...]
因此x 的生命周期仅在初始化完成后才开始。但是在引用的示例中,x 的值是在 x 的初始化完成之前使用的。因此,该使用具有未定义的行为。
我的分析是否正确,如果正确,是否会影响类似的 use-before-initialization 案例,例如
int x = (x = 1);
据我所知,哪些在 C++17 及之前的版本中也得到了很好的定义?
请注意,在 C++17(最终草案)中,生命周期开始的第二个要求是 different:
- 如果对象有非空初始化,则其初始化完成,
由于x 将按照 C++17 的定义进行空初始化(但不是当前草案中的定义),因此在上面给出的示例中的初始化程序中访问它时,它的生命周期已经开始,因此在这两个例子中示例由于 C++17 中 x 的生命周期,没有未定义的行为。
C++17 之前的措辞再次不同,但结果相同。
问题不在于使用不确定值时的未定义行为,例如以下问题:
【问题讨论】:
-
@LanguageLawyer 我不确定我是否正确,尤其是在没有人回答的情况下。如果其他人在这里同意我的观点,我可能会稍后提交一份(或者其他人会在我之前提交),但我不想提交我不确定的问题。
-
@LanguageLawyer:如果工作文件明确地说错了,这不可能是编辑问题。
-
字被P1358改了。
-
@xskxzr 对,同时LanguageLawyer 也提交了editorial issue,似乎已转发给CWG 以澄清意图。
-
@clockw0rk
int x ^= x;的语法格式不正确。您可以使用初始化程序定义变量(即int x = x;,尽管它是UB)或xor 赋值表达式语句(即x ^= x;,尽管如果x是int类型,则它是UB,是默认值-已初始化且未事先分配)。您不能将这两者合二为一。
标签: c++ initialization language-lawyer lifetime c++20