10
  1. 本条款所施加的限制避免了哪些具体问题12.7p3(见下文第一部分)?

  2. 12.7p3(见下文)中显示的示例中,为什么X(this)认为已定义?是不是因为X不在路径中E C D B A

    struct A { };
    struct B : virtual A { };
    struct C : B { };
    struct D : virtual A { D(A*); };
    struct X { X(A*); };
    struct E : C, D, X {
    E() : D(this),  // undefined: upcast from E* to A*
                    // might use path E* - D* - A*
                    // but D is not constructed
                    // D((C*)this), // defined:
                    // E* - C* defined because E() has started
                    // and C* - A* defined because
                    // C fully constructed
          X(this) { // defined: upon construction of X,
                    // C/B/D/A sublattice is fully constructed
        }
    };
    
  3. 请在以下段落的开头找到12.7p3

显式或隐式地将引用 X 类对象的指针(泛左值)转换为指向 X 的直接或间接基类 B 的指针(引用),X 的构造及其所有直接或间接基类的构造从 B 直接或间接派生的那些应该已经开始并且这些类的销毁应该没有完成,否则转换会导致未定义的行为。

是否正确地说X上面提到的所有直接和间接基础的集合不包括B,因此下面的代码定义良好,尽管事实上它Base是的直接基础Derived并且尚未开始?

struct Base{ Base(Base*); };

struct Derived : Base {
    Derived() : Base(this) {};
};
4

1 回答 1

6

使用第 12.7p3 条施加的限制(请参阅下面段落的第一部分)可以避免哪些具体问题?

问题与这样一个事实有关,在某些时候,必须为派生类初始化虚拟指针(指向虚拟表或虚拟基类)。这是在“初始化列表”期间发生的初始化(即,在构造函数主体开始之前),通常是在构造基类之后(例如,在构造基类 B 之后,虚拟表指针和可能的虚拟基指针为类 X 设置,然后初始化数据成员,然后开始构造函数的主体)。通常,在初始化这些指针之前进行向上转换将产生未定义的行为,因为无法为虚拟基执行所需的指针查找,或者无法正确设置虚拟表指针以正确调用虚拟函数.

在 12.7p3(见下文)中显示的示例中,为什么认为 X(this) 已定义?是因为 X 不在路径 ECDBA 中吗?

因为基类构造函数是按照它们在类声明中出现的顺序调用的(即,struct E : C, D, X {)。因为基类 C 和 D 都是构造的,所以它们共同的虚拟基类 A 肯定也是构造的。并且因为X不继承自 class A,这意味着此时执行从 classE到 class的强制转换时有一条完全构造且明确的路径A。这就是为什么这条线是明确定义的。

说上面提到的 X 的所有直接和间接基的集合不包括 B 是否正确,因此下面的代码定义良好,尽管 Base 是 Derived 的直接基并且尚未没有开始?

我不确定我是否遵循您的解释,但我可以告诉您,您显示的代码定义不明确。Base通过转换Derived* this为调用 的构造函数时Base*,派生类中的基类对象尚未构造,因此,转换未定义。

这段代码有意义的唯一方法是,如果层次结构中不包含虚函数或虚基类,在这种情况下,不需要此类代码,因为this指向基类的指针已经隐式发送到基类构造函数。

于 2013-04-30T20:45:19.310 回答