【问题标题】:Binding reference to an object before construction在构造之前绑定对对象的引用
【发布时间】:2015-07-01 13:00:44
【问题描述】:

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

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

【问题讨论】:

  • “引用任何非静态成员 [...] 会导致未定义的行为” - 引用正是引用的作用(因此得名)。
  • @molbdnilo 我明白这一点,但它没有解释会发生什么否则(例如,在构造函数开始为具有平凡构造函数的类之前引用...),这就是问题的重点。
  • “提到它”的意思是提到它(它是英文单词“refer”)。
  • 相关:CWG issue 1530 和 [basic.life]/6
  • @AntonSavin 除非我误解了英语,否则特殊情况“对于具有非平凡构造函数的对象,[...]”暗示在其他情况下行为是明确定义的。

标签: c++ reference language-lawyer c++14


【解决方案1】:

#2 的行为是明确定义的。正如@dyp所提到的,相关段落在[basic.life]中:

将泛左值yobj 绑定到引用是可以的,因为它的存储会在程序的整个过程中持续存在([basic.stc.static]/1),并且引用绑定到一个有效的对象——除了活动之外——它符合 ([dcl.ref]/5) 中的要求。同样,对于您展示的第二个示例,只要不对A 子对象的成员执行任何操作,上述段落也适用,因为C 的构造函数是在this 所指的分配存储上调用的。

【讨论】:

    【解决方案2】:

    [...] 示例中没有提及引用 [...]

    你的意思是,除了

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

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

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

    因为你是:

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

    另外,为此:

    最重要的是,上述段落并不暗示否则行为是明确定义的。

    你是对的,它没有:

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

    【讨论】:

    • #3 调用 UB,这是毫无疑问的。但是#2(直观地)等同于Y* p2 = &yobj,在标准示例中明确标记为定义明确(但在规范文本中没有)
    • 是吗?如果不是yobj,那么“基类”是什么意思?
    • 由于虚拟基类的原因,引用非静态数据成员和基类可能是UB。 vtable 是在构建过程中设置的,因此在构建开始之前,您既不能访问其成员,也不能访问虚拟基类本身。
    • 如果“基类”表示“对象本身”(== yobj),那么引用yobj 本身会产生 UB。我并不完全清楚“基类”到底是什么,特别是因为它没有提到“基类的成员”,而是基类本身。
    • 据我所知,base class 从不指代类型本身。不过,这里应该是基类子对象
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 2022-11-21
    相关资源
    最近更新 更多