Title
Can cast to virtual base class be done on partially-constructed object?
Status
open
Section
6.7.4 [basic.life]
Submitter
Judy Ward

Created on 2003-06-02.00:00:00 last changed 262 months ago

Messages

Date: 2003-06-02.00:00:00

Consider

  extern "C" int printf (const char *,...);

  struct Base { Base();};
  struct Derived: virtual public Base {
     Derived() {;}
  };

  Derived d;
  extern Derived& obj = d;

  int i;

  Base::Base() {
    if ((Base *) &obj) i = 4;
    printf ("i=%d\n", i);
  }

  int main() { return 0; }

11.9.5 [class.cdtor] paragraph 2 makes this valid, but 6.7.4 [basic.life] paragraph 5 implies that it isn't valid.

Steve Adamczyk: A second issue:

  extern "C" int printf(const char *,...);
  struct A                      { virtual ~A(); int x; };
  struct B : public virtual A   { };
  struct C : public B           { C(int); };
  struct D : public C           { D(); };

  int main()                    { D t; printf("passed\n");return 0; }

  A::~A()                       {}
  C::C(int)                     {}
  D::D() : C(this->x)           {}

Core issue 52 almost, but not quite, says that in evaluating "this->x" you do a cast to the virtual base class A, which would be an error according to 11.9.5 [class.cdtor] paragraph 2 because the base class B constructor hasn't started yet. 7.6.1.5 [expr.ref] should be clarified to say that the cast does need to get done.

James Kanze submitted the same issue via comp.std.c++ on 11 July 2003:

Richard Smith: Nonsense. You can use "this" perfectly happily in a constructor, just be careful that (a) you're not using any members that are not fully initialised, and (b) if you're calling virtual functions you know exactly what you're doing.

In practice, and I think in intent, you are right. However, the standard makes some pretty stringent restrictions in 6.7.4 [basic.life]. To start with, it says (in paragraph 1):

The lifetime of an object is a runtime property of the object. The lifetime of an object of type T begins when:
  • storage with the proper alignment and size for type T is obtained, and
  • if T is a class type with a non-trivial constructor, the constructor calls has COMPLETED.
The lifetime of an object of type T ends when:
  • if T is a class type with a non-trivial destructor, the destructor call STARTS, or
  • the storage which the object occupies is reused or released.
(Emphasis added.) Then when we get down to paragraph 5, it says:

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated [which sounds to me like it would include in the constructor, given the text above] or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. [...] If the object will be or was of a non-POD class type, the program has undefined behavior if:

[...]

  • the pointer is implicitly converted to a pointer to a base class type, or [...]

I can't find any exceptions for the this pointer.

Note that calling a non-static function in the base class, or even constructing the base class in initializer list, involves an implicit conversion of this to a pointer to the base class. Thus undefined behavior. I'm sure that this wasn't the intent, but it would seem to be what this paragraph is saying.

History
Date User Action Args
2003-06-02 00:00:00admincreate