c++ 了解起始对象构造过程和UB

4zcjmb1e  于 2023-05-08  发布在  其他
关注(0)|答案(1)|浏览(166)

12.7/3 N3797中给出了以下示例

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
    }
};

示例的规则:
要显式或隐式地将指向类X的对象的指针(glvalue)转换为指向X的直接或间接基类B的指针(引用),X的构造以及直接或间接从B派生的所有直接或间接基类的构造应该已经开始,并且这些类的析构应该尚未完成,否则转换将导致未定义的行为。
但是考虑一下对示例的一点修改:

struct A { };
struct HD { }; // will be used as a base for D
struct B : virtual A { };
struct C : B { };
struct D : HD, virtual A { D(A*); };
struct X { X(A*); };
struct E : C, D, X {
    E() : D(this), // 1. Is there undefined behavior? 
                   // I think, there isn't.
    X(this) {
    }
};

我认为//1没有UB,因为我们有类HD的基类子对象,它是在调用D(A*)构造函数之前构造的。也就是说,在D(A*)调用时,D的构造已经开始。
我的推理正确吗?

q8l4jmvw

q8l4jmvw1#

HD并不像OP声明的那样,在调用D::D(A*)之前构造。事件的顺序是先初始化D::D(A*)的参数,然后构造D的第一个基类HD,然后进入D::D(A*)的主体。(D的虚基类A已经在EC基类的构造过程中构造好了,所以不会再构造它了。)
因此,问题的前提是无效的。OP的示例具有UB,就像标准中的原始示例一样;从E*A*的转换(需要初始化D::D(A*)的参数)过早发生,并导致UB。

相关问题