That is, is something like this always legal?
struct Derived;
struct Base { Base(Derived*); };
struct Derived : Base { Derived() : Base(this) { } };
Base::Base(Derived *self) {
if(static_cast<Base*>(self) != this) std::terminate();
}
int main() {
Derived d; // is this well-defined to never call terminate?
}
At the point that the static_cast
is evaluated, self
does not yet point to a Derived
object—that object is under construction. E.g. if Derived
had data members, their constructors would not have been called. Is the cast still guaranteed to be defined behavior, resulting in a pointer equivalent to Base
's this
(which does point to a fully constructed Base
base class subobject)?
A standard quote that I think gets close to answering this is [conv.ptr]/3
.
...The result of the conversion is a pointer to the base class subobject of the derived class object. ...
But I think there is no derived class object yet, so what happens? If it is indeed undefined, does the answer change for self != static_cast<Derived*>(this)
?
(Clang and GCC compile and run this "as expected".)
This is fine: [class.cdtor]/3
says
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 classB
ofX
, the construction ofX
and the construction of all of its direct or indirect bases that directly or indirectly derive fromB
shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. ...
It requires that the source type (and any other bases inheriting from the destination type) have begun its constructor and not have finished its destructor. Even the initializer for the base counts as beginning the derived constructor; the standard contains a very similar example.
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