Annex F (informative) Core undefined behavior [ub]

F.6 [class]: Classes [ub.class]

F.6.1[ub:class.dtor.no.longer.exists]
Specified in: [class.dtor]

Once a destructor is invoked for an object, the object's lifetime has ended; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended.
[Example 1: struct A { ~A() {} }; int main() { A a; a.~A(); } // undefined behavior, lifetime of a already ended before implicit destructor — end example]

F.6.2[ub:class.abstract.pure.virtual]
Specified in: [class.abstract]

Calling a pure virtual function from a constructor or destructor in an abstract class has undefined behavior.
[Example 1: struct B { virtual void f() = 0; B() { f(); // undefined behavior, f is pure virtual and is being called from the constructor } }; struct D : B { void f() override; }; — end example]

F.6.3[ub:class.base.init.mem.fun]
Specified in: [class.base.init]

Calling a member function before all the mem-initializers for base classes have completed has undefined behavior.
[Example 1: class A { public: A(int); }; class B : public A { int j; public: int f(); B() : A(f()), // undefined behavior, calls member function but base A not yet initialized j(f()) {} // defined, bases are all initialized }; class C { public: C(int); }; class D : public B, C { int i; public: D() : C(f()), // undefined behavior, calls member function but base C not yet initialized i(f()) {} // defined, bases are all initialized }; — end example]

F.6.4[ub:class.cdtor.before.ctor]
Specified in: [class.cdtor]

For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.
[Example 1: struct W { int j; }; struct X : public virtual W {}; struct Y { int *p; X x; Y() : p(&x.j) { // undefined behavior, x is not yet constructed } }; — end example]

F.6.5[ub:class.cdtor.after.dtor]
Specified in: [class.cdtor]

For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution has undefined behavior.
[Example 1: struct X { int i; ~X(); // non-trivial }; X& g() { static X x; return x; } void f() { X* px = &g(); px->~X(); int j = px->i; // undefined behavior } — end example]

F.6.6[ub:class.cdtor.convert.pointer]
Specified in: [class.cdtor]

Converting a pointer to a base class of an object, when construction of that object has not started or destruction of that object has finished, has undefined behavior.
[Example 1: 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 behavior, upcast from E* to A* might use path E* D* A* // but D is not constructed // “D((C*)this)'' would be defined, E* C* is defined because E() has started, // and C* A* is defined because C is fully constructed X(this) {} // defined, upon construction of X, C/B/D/A sublattice is fully constructed }; — end example]

F.6.7[ub:class.cdtor.form.pointer]
Specified in: [class.cdtor]

When forming a pointer to a direct non-static member of a class, construction must have started and destruction must not have finished otherwise the behavior is undefined.
[Example 1: struct A { int i = 0; }; struct B { int *p; A a; B() : p(&a.i) {} // undefined behavior }; — end example]

F.6.8[ub:class.cdtor.virtual.not.x]
Specified in: [class.cdtor]

When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class.
If the virtual function call uses an explicit class member access ([expr.ref]) and the object expression refers to the complete object of x or one of that object's base class subobjects but not x or one of its base class subobjects, the behavior is undefined.
[Example 1: struct V { virtual void f(); virtual void g(); }; struct A : virtual V { virtual void f(); }; struct B : virtual V { virtual void g(); B(V *, A *); }; struct D : A, B { virtual void f(); virtual void g(); D() : B((A *)this, this) {} }; B::B(V *v, A *a) { f(); // calls V​::​f, not A​::​f g(); // calls B​::​g, not D​::​g v->g(); // v is base of B, the call is defined, calls B​::​g a->f(); // undefined behavior, a's type not a base of B } — end example]

F.6.9[ub:class.cdtor.typeid]
Specified in: [class.cdtor]

If the operand of typeid refers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the behavior is undefined.
[Example 1: struct V { virtual void f(); }; struct A : virtual V {}; struct B : virtual V { B(V *, A *); }; struct D : A, B { D() : B((A *)this, this) {} }; B::B(V *v, A *a) { typeid(*this); // std​::​type_info for B typeid(*v); // defined, *v has type V, a base of B yields std​::​type_info for B typeid(*a); // undefined behavior, type A not a base of B } — end example]

F.6.10[ub:class.cdtor.dynamic.cast]
Specified in: [class.cdtor]

If the operand of the dynamic_cast refers to the object under construction or destruction and the static type of the operand is not a pointer to or object of the constructor or destructor's own class or one of its bases, the dynamic_cast results in undefined behavior.
[Example 1: struct V { virtual void f(); }; struct A : virtual V {}; struct B : virtual V { B(V *, A *); }; struct D : A, B { D() : B((A *)this, this) {} }; B::B(V *v, A *a) { dynamic_cast<B *>(v); // defined, v of type V*, V base of B results in B* dynamic_cast<B *>(a); // undefined behavior, a has type A*, A not a base of B } — end example]