6

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".)

HTNW
  • 27,182
  • 1
  • 32
  • 60
  • There are 2 subquestions: is self guaranteed to equal this, and would dereferencing `static_cast(self)` be well-defined. – spectras Aug 07 '21 at 02:03
  • _At the point that the `static_cast` is evaluated, `self` does not yet point to a `Derived` object_ Why? Where does `self` point to then? – Language Lawyer Aug 07 '21 at 10:26
  • @LanguageLawyer It points to uninitialized storage where a `Derived` object will eventually begin its lifetime, no? Is it possible that my quote can apply to objects that are not within their lifetime? – HTNW Aug 07 '21 at 15:17
  • _It points to uninitialized storage_ What does it mean in terms of https://timsong-cpp.github.io/cppwp/n4868/basic.compound#3.sentence-8? _Is it possible that my quote can apply to objects that are not within their lifetime?_ Unfortunately, no, because of https://timsong-cpp.github.io/cppwp/n4868/basic.life#6.4 – Language Lawyer Aug 07 '21 at 16:51
  • @LanguageLawyer Perhaps it would be better for me to reword that as "the pointer points to an object that is not within its lifetime." I guess the standard doesn't really define what it means for an object to "exist". Also, I'm not sure that paragraph's restriction quite applies: it says that for the case of an object under construction you go to the subclause cited in the answer here ("otherwise"). Or do you say that it still applies? Then would `Base *ptr = self; if(self != this) std::terminate();` be correct whereas the code in the question is not? – HTNW Aug 07 '21 at 17:02
  • _I guess the standard doesn't really define what it means for an object to "exist"._ [It says how an object is created](https://timsong-cpp.github.io/cppwp/n4868/intro.object#1.sentence-2). _Also, I'm not sure that paragraph's restriction quite applies_ I've thought you're asking about the case when an object is outside lifetime and not under construction. – Language Lawyer Aug 07 '21 at 17:40
  • @LanguageLawyer An object under construction is outside of its lifetime. I ask about exactly such an object. – HTNW Aug 07 '21 at 17:50
  • _An object under construction is outside of its lifetime._ IKR. An interesting question: is «under construction» and «during the period of construction» the same thing? – Language Lawyer Aug 07 '21 at 22:23

1 Answers1

2

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 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. ...

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.

HTNW
  • 27,182
  • 1
  • 32
  • 60
Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • 1
    CWG1517 proposed resolution says that period of construction begins after base subobjects constructions finish. On the other OTOH, the resolution changes paragraph 3 to apply only to virtual bases. – Language Lawyer Aug 07 '21 at 10:28