在构造之前绑定对象的引用

Ant*_*vin 21 c++ reference language-lawyer c++14

以下代码的行为是否定义明确?

struct X { int i; }; // trivial
struct Y : X { Y(){} }; // non-trivial

extern X xobj;
int& r1 = xobj.i; // #1
X xobj;

extern Y yobj;
Y& r2 = yobj;     // #2
// int& r3 = yobj.i; // #3 - this is UB according to the standard
Y yobj;
Run Code Online (Sandbox Code Playgroud)

此代码的灵感来自C++标准中的示例,即草案N4140 [class.cdtor]/1.

那段是那段内容:

对于具有非平凡构造函数的对象,在构造函数开始执行之前引用对象的任何非静态成员或基类会导致未定义的行为.对于具有非平凡析构函数的对象,在析构函数完成执行后引用对象的任何非静态成员或基类会导致未定义的行为.

下面是一个示例,其中显示了指针如何绑定到对象,也可能不绑定到对象.

如此直观地看起来#1并且#2定义明确,#3如果取消注释则调用UB,但是,首先,示例不是规范性的,其次,示例中没有提及引用,第三个也是最重要的,上面的段落没有意味着否则行为是明确的.或者是吗?或者我错过了标准中的另一个相关引用?

编辑:如果对象具有静态存储持续时间,答案可能(可以说)是肯定的,但它们也可以是本地的,例如:

struct A { A(){} };
struct B { B(A&){} };

struct C {
    B b;
    A a;
    C() : b(a) {}
};

int main() {
    C c;
}
Run Code Online (Sandbox Code Playgroud)

实际上这是这个问题的最初灵感,请参阅构造函数初始化列表中的循环依赖

Sig*_*uza 0

[...]示例中没有提及参考文献[...]

你的意思是,除了

[...]的是任何非静态成员[...]

从您引用的段落来看,我想说这一行会导致未定义的行为:

int& r3 = yobj.i; // #3
Run Code Online (Sandbox Code Playgroud)

因为你是:

[...]在构造函数开始执行之前引用对象的任何非静态成员或基类[.]

另外,为此:

最重要的是,上面的段落并不意味着行为是明确定义的。

你是对的,事实并非如此:

当本国际标准省略任何明确的行为定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。