Code:
#include
using std::cout;
using std::endl;
struct A
{
virtual void foo()
{
cout << \"A\" << endl;
}
§1.8 [intro.object]/p2-3:
Objects can contain other objects, called subobjects. A subobject can be a member subobject (9.2), a base class subobject (Clause 10), or an array element. An object that is not a subobject of any other object is called a complete object.
For every object
x
, there is some object called the complete object ofx
, determined as follows:
- If
x
is a complete object, thenx
is the complete object ofx
.- Otherwise, the complete object of
x
is the complete object of the (unique) object that containsx
.
In essence, the sentence you cited makes doing static_cast
in B
's constructor undefined behavior in your code, even if the complete object being constructed is a C
. The standard actually provides a pretty good example here:
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 well-defined, calls B::g
a->f(); // undefined behavior, a’s type not a base of B
}
In fact, you can already see the undefined behavior show up in this example if you run it: Ideone's compiler (GCC) actually calls V::f()
on the a->f();
line, even though the pointer is referring to a fully constructed A
subobject.