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
Xto a pointer (reference) to a direct or indirect base classBofX, the construction ofXand the construction of all of its direct or indirect bases that directly or indirectly derive fromBshall 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