构造函数初始化列表中的循环依赖项

问题描述:

以下定义明确吗?构造函数初始化列表中的循环依赖项

class A; 
class B; 

// define A, which takes B& in constructor 
// define B, which takes A& in constructor 

class C 
{ 
    A a; 
    B b; 
public: 
    C() : a(b), b(a) { /* stuff with a and b */ } 
} 

完整的示例在ideone.com

只要AB的构造函数对引用没有做任何操作,它是安全/定义好的吗?

+1

为什么downvote?让我知道我该如何改进这个问题。 – Claudiu

+0

我不认为编译器允许你执行'a(b)',因为当'a'被初始化时,'b'还没有被初始化。 – user3528438

+0

@ user3528438:确实如此,ideone示例编译并运行。但是,这是因为我很幸运,还是因为它在标准中有明确的定义? – Claudiu

N4140 [class.cdtor]/1读取:

对于具有一个非平凡构造方法的对象,参考任何非静态成员或基类的构造函数前的对象 的开始执行结果在未定义的行为。对于具有非平凡的析构函数的对象,在析构函数完成后引用该对象的任何非静态成员或基类 执行会导致未定义的行为。

虽然这段经文本身并不意味着行为是否定义良好,但下面的例子说明了这一点。下面是摘录:

struct B : public A { int j; Y y; }; // non-trivial 
extern B bobj; 
B* pb = &bobj; // OK 

因此,答案是:是的,你的情况的行为,如果你没有在A构造指的是b成员或基类是明确界定。

+0

这足够直观。因此,[user3528438的示例](https://ideone.com/V5uPyr)是未定义的行为,因为在m_a的构造函数运行之前访问了m_a.m_value。感谢您挖掘参考资料! – Claudiu

+0

@Claudiu对,它绝对是UB。 –

+0

您可以将一个*引用*绑定到尚未构造的对象吗?指针和引用的语义存在差异。例如,'int * p = nullptr; int&r = * p;'被认为是UB,但是'int * r = &*p;'可以说不是UB(例如C明确地允许它)。 – dyp