这不是未定义行为。虽然
foo
未初始化,但您正在以标准允许的方式使用它。在为对象分配空间但尚未完全初始化之后,您可以有限地使用它。绑定对该变量的引用和获取其地址都是允许的。
这是由
defect report 363: Initialization of class from self所涵盖的。
And if so, what is the semantics of the self-initialization of UDT?
For example
#include <stdio.h>
struct A {
A() { printf("A::A() %p\n", this); }
A(const A& a) { printf("A::A(const A&) %p %p\n", this, &a); }
~A() { printf("A::~A() %p\n", this); }
};
int main()
{
A a=a;
}
can be compiled and prints:
A::A(const A&) 0253FDD8 0253FDD8
A::~A() 0253FDD8
and the resolution was:
3.8 [basic.life] paragraph 6 indicates that the references here are valid. It's permitted to take the address of a class object before it is fully initialized, and it's permitted to pass it as an argument to a reference parameter as long as the reference can bind directly. Except for the failure to cast the pointers to void * for the %p in the printfs, these examples are standard-conforming.
The full quote of section 3.8
[basic.life] from the draft C++14 standard is as follows:
类似地,在对象的生命周期开始之前,但在分配了对象将要占用的存储空间之后,或者在对象的生命周期结束之后,在重新使用或释放了对象所占用的存储空间之前,任何引用原始对象的glvalue都可以使用,但仅有限制性地。有关正在构建或销毁的对象,请参见12.7。否则,这样的glvalue是指向已分配存储空间(3.7.4.2)的,并且使用不依赖于其值的glvalue属性是定义良好的。如果程序满足以下条件,则具有未定义行为:
- 对此类glvalue应用lvalue-to-rvalue转换(4.1);
- 使用glvalue访问非静态数据成员或调用对象的非静态成员函数;
- 将glvalue绑定到虚基类的引用(8.5.3);
- 将glvalue用作dynamic_cast(5.2.7)的操作数或typeid的操作数。
我们没有做任何与上述内容定义的未定义行为相关的事情,与foo
无关。
如果我们尝试在Clang中执行此操作,我们会看到一个不祥的警告(请现场查看):
警告:当在其自身初始化期间使用未初始化的变量'foo'时[-Wuninitialized]
这是一个有效的警告,因为从未初始化的自动变量产生不确定值是未定义行为。然而,在这种情况下,您只是在构造函数中绑定引用并取变量的地址,这不会产生不确定值,因此是有效的。另一方面,根据C++11标准草案的以下自初始化示例:
int x = x ;
does invoke undefined behavior.
活跃问题453:引用只能绑定到“有效”的对象似乎也相关,但仍未解决。最初提出的语言与缺陷报告363一致。
this
一样,具有所有的缺陷。 - Kerrek SBsize_t x = sizeof(x)
吗?对象的构造函数在分配内存时被调用(来自未指定的源)。只要您仅依赖于存储的属性,而不依赖于任何值的解释,事情就应该是安全的。 - MSalters