int foo = foo;
可以编译通过。C++标准的哪一部分允许这样做?
3.3.1 声明点 [basic.scope.pdecl]
一个名称的声明点是在它的完整声明符之后(第8条款),初始化程序(如果有)之前立即发生。
如果声明位于文件作用域,则行为是定义良好的。如果您在函数作用域中声明,并且稍后使用foo
[在这种情况下将被初始化为某些未指定的值],则行为将是未定义的。
s/program has/program may have/
; s/should not/possibly should not/
. - Lightness Races in Orbitfoo
“以后” 并不重要;初始化本身就会调用 UB!不是吗? - Lightness Races in OrbitB
来初始化 A
(而且 A
和 B
恰好是同一个对象)。 - Lightness Races in Orbit这个?
int main() {
int foo = foo;
}
根据[basic.scope.pdecl]
,在完整的声明符(第8条款)之后,变量名的声明点立即存在于其初始值(如果有)之前,因此foo
对象在=
之后确实存在。
然而,由于在右侧使用了未初始化的值,整个程序是未定义的:
int x = x;
这里[..]x
用其自身(不确定的)值初始化。
并且:
尽管标准中"推断和规范不明确",对RHS表达式foo
执行了一个左值到右值的转换。
并且 ([conv.lval]
):
非函数、非数组类型T的左值(3.10)可以转换为右值。如果T是不完整的类型,则需要进行此转换的程序是不良形式的。如果左值所引用的对象不是类型T的对象,也不是从T派生的类型的对象,或者如果对象未初始化,则需要进行此转换的程序具有未定义行为。
在适当的警告级别下,你将会收到相关的提示;然而,调用未定义行为的程序是允许编译的。只是当你运行它们时,它们可能会做任何事情。
或者,这个怎么样?
int foo = foo;
int main() {}
foo
是一个"全局变量"。根据 [basic.start.init]
,它们在进行任何其他初始化之前首先进行零初始化:
因此,您将获得一个值为0的具有静态存储期(3.7.1)的对象应在进行任何其他初始化之前进行零初始化(8.5)。
int foo
;在这一点上,根据上面的[basic.scope.pdecl]
和[stmt.decl]
,它是有效的:
然后,您将其值初始化为所有具有静态存储期(3.7.1)的本地对象的零初始化(8.5)是在进行任何其他初始化之前执行的。[..]
foo
(它本身),即0。为了全面考虑,这里提供第三个也是最后一个案例:
int foo = 42;
int main() {
int foo = foo;
}
不幸的是,这与第一种情况相同。由于当初始化器被评估时,局部变量foo
已经声明并在作用域内,因此初始化器使用局部变量foo
,你仍然会遇到未定义行为。全局变量foo
没有被使用。
foo
声明发生在命名空间范围内),他需要说明这一点,因为这会完全改变问题的性质。 - Lightness Races in Orbit
int x = x;
是否UB?和C++14中C++标准在使用不确定值和未定义行为方面是否有所改变? - Shafik Yaghmour