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

时间:2015-04-23 11:16:14

标签: 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;

此代码的灵感来自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;
}

实际上这是这个问题的最初灵感,请参阅Circular dependency in constructor initialization list

2 个答案:

答案 0 :(得分:2)

The behavior of #2 is definitely well-defined. As mentioned by @dyp, the relevant paragraph is in [basic.life]: Binding the glvalue yobj to a reference is fine, since its storage lasts throughout the duration of the program ([basic.stc.static]/1) and the reference is bound to a valid object - aliveness aside - which meets the requirement in ([dcl.ref]/5). Similarly, for the second example you showed, as long as no operation is performed on members of the A subobject, the above paragraph applies as well since the constructor of C is called on the allocated storage this refers to.

答案 1 :(得分:0)

  

[...]在示例[...]

中没有提及引用

你的意思是,除了

  

[...] 引用到任何非静态成员[...]

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

int& r3 = yobj.i; // #3

因为你是:

  

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

另外,为此:

  

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

你是对的,它没有:

  

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