What are the specific problems that are being avoided with the restrictions imposed by this clause 12.7p3
(see the first part of paragraph below)?
In the example shown in 12.7p3
(see below) why X(this)
is considered defined? Is it because X
is not in the path 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
}
};
Please find below the start of paragraph 12.7p3
:
To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior.
Is it correct to say that the set of all direct and indirect bases of X
, mentioned above don't include B
, and because of this the code below is well defined, notwithstanding the fact that Base
is a direct base of Derived
and has yet not started?
struct Base{ Base(Base*); };
struct Derived : Base {
Derived() : Base(this) {};
};
What are the specific problems that are being avoided with the restrictions imposed by this clause 12.7p3 (see the first part of paragraph below)?
The problems have to do with the fact that, at some point, the virtual pointers (to virtual table or virtual base classes) must be initialized for the derived class. This is an initialization that occurs during the "initialization list" (i.e., before the constructor-body begins), usually right after the base class was constructed (e.g., after base class B is constructed, the virtual table pointer and possibly virtual base pointer is set for class X, and then the data members are initialized, and then the constructor's body begins). In general, doing an up-cast before those pointers are initialized will yield undefined behaviour, either because the pointer lookup required cannot be done for a virtual base, or the virtual table pointer will not be set correctly for the virtual functions to be called correctly.
In the example shown in 12.7p3 (see below) why X(this) is considered defined? Is it because X is not in the path E C D B A ?
Because the base class constructors are called in the order that they appear in the class declaration (i.e., struct E : C, D, X {
). Because the base classes C and D are both constructed, their common virtual base class A is certainly also constructed. And because X
does not inherit from class A
, it means that there is a fully-constructed and unambiguous path when performing the cast from class E
to class A
at this point. That is why that line is well-defined.
Is it correct to say that the set of all direct and indirect bases of X, mentioned above don't include B, and because of this the code below is well defined, notwithstanding the fact that Base is a direct base of Derived and has yet not started?
I'm not sure I follow your explanation, but I can tell you that the code you showed is not well-defined. When calling the constructor of Base
by casting Derived* this
to Base*
, the base-class object in the derived class is not yet constructed, and thus, the cast is undefined.
The only way that this code could make sense is if there were no virtual functions or virtual base classes involved in the hierarchy, in which case, such code would not be needed since the this
pointer to the base class is already sent, implicitly, to the base-class constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With