Topic: Dynamic type. When is it assigned ?


Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: Fri, 17 Feb 1995 13:46:57 GMT
Raw View
In article <3hqaht$nv0@silver.jba.co.uk>,
Jonathan de Boyne Pollard <JdeBP@jba.co.uk> wrote:
>When is the dynamic type of an instance assigned and removed in its
>constructor and destructor ?  Most of the documentation available to me right
>at this moment is pretty vague on the subject.

 This question has been the subject of extensive and intensive
research by the core group of the committee.

 The answer is not as simple as you think, it is not
just a matter of "the" dynamic type. The dynamic type _changes_
during construction, and there is more than one entity
during construction that has a dynamic type. Separately,
being able to address components is an issue. When can you
address a member of a virtual base of a member?

 The dynamic type is established after the bases have
been fully initialised but before the members. (As far as
I can remember)

 The original resolution for addressability was

 "everything is addressable immediately after
 evaluating the arguments of the constructor of
 the most derived class"

but that was not put in the Working Paper because it precludes
constructors setting up virtual base pointers.

>It seems that this would be the sort of thing that would be implementation
>defined, or possibly not be required to be defined at all.

 No, the committee has a much tighter requirement.

 Do you want all virtual function calls in constructors
to fail?

>Some experimentation leads me to believe that most implementations assign
>dynamic type at the start of the constructor body after any member or base
>class initialisers, and remove it at the end of the destructor body before
>any member or base class destructors.

>Are there weasel words to accommodate both ?  Or are there more rigid
>guarantees ?
>
>By the way, I couldn't determine whether this was properly a standards or
>a language question.  Please followup as you think appropriate.


 Standards question. Rigid guarrantees.
Text of current WP is your only accurate guide. (Sorry)

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 20 Feb 1995 11:23:14 GMT
Raw View
JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:

>When is the dynamic type of an instance assigned and removed in its
>constructor and destructor ?  Most of the documentation available to me right
>at this moment is pretty vague on the subject.

This was discussed by the committee at the Valley Forge meeting
(November last year).

>It seems that this would be the sort of thing that would be implementation
>defined, or possibly not be required to be defined at all.  We would be
>guaranteed that the dynamic type of an instance would be correct from after
>the completion of the constructor up until the start of the destructor.
>But nothing that I can find will actually confirm this.

The latest working paper does guarantee that.

>Some experimentation leads me to believe that most implementations assign
>dynamic type at the start of the constructor body after any member or base
>class initialisers, and remove it at the end of the destructor body before
>any member or base class destructors.

According to the latest working paper, the dynamic type must be
assigned before the member initializers are executed.
Conversely, it should not be removed until after the member destructors
are executed.  So it sounds like those implementations don't quite conform.
However, implementations that assign dynamic type after any base class
initialisers but before any member initializers, and remove it after
any member destructors but before any base class destructors, would be
conforming.

>However, this is not universally the case.  At least two of the C++
>compilers in my possession (both of which are DirectToSOM C++ compilers)
>assign the dynamic type to an instance before any member or base class
>initialisers are invoked.
>
>Are there weasel words to accommodate both ?  Or are there more rigid
>guarantees ?

There are weasel words to accomodate both (with the correction to the
first group that I mentioned above).
We spent quite a lot of time finding the right weasel words ;-)

The appropriate parts of the latest working paper follow.

8 Member functions (including virtual member functions, _class.virtual_)
  can  be called for an object under construction.  Similarly, an object
  under  construction  can  be  the  operand  of  the  typeid   operator
  (_expr.typeid_)  or of a dynamic_cast (_expr.dynamic.cast_).  However,
  if these operations are performed in a ctor-intializer (or in a  func
  tion  called directly or indirectly from a ctor-intializer) before all
  the mem-initializers for base classes have completed,  the  result  of
  the operation is undefined.  For example:
          class A {
          public:
                  A(int);
          };

          class B : public A {
                  int j;
          public:
                  int f();
          B() : A(f()),           // undefined: calls member function
                                  // but base A not yet initialized
                  j(f()) { }      // well-defined: bases are all initialized
          };
          class C {
          public:
                  C(int);
          };
          class D : public B, C {
                  int i;
          public:
                  D() : C(f()),   // undefined: calls member function
                                  // but base C not yet initialized
                  i(f()) {}       // well-defined: bases are all initialized
          };

9 _class.cdtor_  describes  the result of virtual function calls, typeid
  and dynamic_casts during construction for the well-defined cases; that
  is,  describes  the  polymorphic behavior of an object under construc
  tion.

  12.7  Construction and destruction                       [class.cdtor]

1 For an object of non-POD class type (_class_), before the  constructor
  begins  execution  and after the destructor finishes execution, refer
  ring to any nonstatic member or base class of the  object  results  in
  undefined behavior.  For example,
          struct X { int i; };
          struct Y : X { };
          struct A { int a; };
          struct B : public A { int j; Y y; };
          extern B bobj;
          B* pb = &bobj;          // ok
          int* p1 = &bobj.a;      // undefined, refers to base class member
          int* p2 = &bobj.y.i;    // undefined, refers to member's member
          A* pa = &bobj;          // undefined, upcast to a base class type
          B bobj;                 // definition of bobj
          extern X xobj;
          int* p3 = &xobj.i;      // Ok, X is a POD class
          X xobj;

2 Example

          struct W { int j; };
          struct X : public virtual W { };
          struct Y {
                  int *p;
                  X x;
                  Y() : p(&x.j)    // undefined, x is not yet constructed
                  { }
          };

3 To  explicitly or implicitly convert a pointer to an object of class X
  to a pointer to a direct or indirect base class B, the construction of
  X  and  the  construction  of all of its direct or indirect bases that
  directly or indirectly derive from B and  which  are  also  direct  or
  indirect base classes of X6) shall have started and the destruction of
  these  classes  shall  not  have  completed, otherwise the computation
  results in undefined behavior.  To form a pointer  to  a  direct  non
  static member of an object X given a pointer to X, the construction of
  X shall have started and the destruction of  X  shall  not  have  com
  pleted,  otherwise the computation results in undefined behavior.  For
  example,
          struct A { };
          struct B : virtual A { };
          struct C : B { };
          struct D : virtual A { D(A*); };
          struct X { X(A*); };
          struct E : C, D, X {
                  E() : D(this),  // undefined: upcast from E* to A*
                                  // might use path E* -> D* -> A*
                                  // but D is not constructed
                  // D((C*)this), // defined:
                                  // E* -> C* defined because E() has started
                                  // and C* -> A* defined because
                                  // C fully constructed
                  X(this)         // defined: upon construction of X,
                                  // C/B/D/A sublattice is fully constructed
                  { }
          };

4 Member functions, including virtual functions  (_class.virtual_),  can
  be  called  during  construction  or  destruction (_class.base.init_).
  When a virtual function is called directly or indirectly from  a  con
  structor  (including  from its ctor-initializer) or from a destructor,
  the function called is the one defined in the constructor or  destruc
  tor's  own class or in one of its bases, but not a function overriding
  it in a class derived from the constructor or  destructor's  class  or
  overriding  it in one of the other base classes of the complete object
  (_intro.object_).  If the virtual function call uses an explicit class
  member access (_expr.ref_) and the object-expression's type is neither
  the constructor or destructor's own class or one  of  its  bases,  the
  _________________________
  6)  If  X  is  itself a base class, not all classes derived from B are
  necessarily base classes of X.

  result of the call is undefined.  For example,
          class V {
          public:
                  virtual void f();
                  virtual void g();
          };
          class A : public virtual V {
          public:
                  virtual void f();
          };
          class B : public virtual V {
          public:
                  virtual void g();
                  B(V*, A*);
          };
          class D : public A, B {
          public:
                  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
          }

5 The typeid operator (_expr.typeid_) can be used during construction or
  destruction (_class.base.init_).  When typeid is used in a constructor
  (including  in  its ctor-initializer) or in a destructor, or used in a
  function  called  (directly  or  indirectly)  from  a  constructor  or
  destructor,  if  the operand of typeid refers to the object under con
  struction or destruction, typeid yields the type_info representing the
  constructor or destructor's class.  If the operand of typeid refers to
  the object under construction or destruction and the  static  type  of
  the  operand  is neither the constructor or destructor's class nor one
  of its bases, the result of typeid is undefined.

6 Dynamic_casts (_expr.dynamic.cast_) can be used during construction or
  destruction (_class.base.init_). When a dynamic_cast is used in a con
  structor (including in its ctor-initializer) or in  a  destructor,  or
  used  in a function called (directly or indirectly) from a constructor
  or destructor, if the operand of the dynamic_cast refers to the object
  under  construction  or destruction, this object is considered to be a
  complete object that has the type of the constructor  or  destructor's
  class.   If the operand of the dynamic_cast refers to the object under
  construction or destruction and the static type of the operand is  not
  a pointer to or object of the constructor or destructor's own class or
  one of its bases, the dynamic_cast results in undefined behavior.

7 Example

          class V {
          public:
                  virtual void f();
          };
          class A : public virtual V { };
          class B : public virtual V {
          public:
                  B(V*, A*);
          };
          class D : public A, B {
          public:
                  D() : B((A*)this, this) { }
          };
          B::B(V* v, A* a) {
                  typeid(this);   // type_info for B
                  typeid(*v);     // well-defined: *v has type V, a base of B
                                  // yields type_info for B
                  typeid(*a);     // undefined behavior: type A not a base of B
                  dynamic_cast<B*>(v); // well-defined: v of type V*, V base of
 B
                                  // results in B*
                  dynamic_cast<B*>(a); // undefined behavior,
                                  // a has type A*, A not a base of B
          }

--
Fergus Henderson - fjh@munta.cs.mu.oz.au
all [L] (programming_language(L), L \= "Mercury") => better("Mercury", L) ;-)




Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: Mon, 20 Feb 1995 19:54:08 GMT
Raw View
In article <9505122.20318@mulga.cs.mu.OZ.AU>,
Fergus Henderson <fjh@munta.cs.mu.OZ.AU> wrote:
>JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:
>
>>When is the dynamic type of an instance assigned and removed in its
>>constructor and destructor ?  Most of the documentation available to me right
>>at this moment is pretty vague on the subject.
>
>According to the latest working paper, the dynamic type must be
>assigned before the member initializers are executed.
>Conversely, it should not be removed until after the member destructors
>are executed.  So it sounds like those implementations don't quite conform.
>However, implementations that assign dynamic type after any base class
>initialisers but before any member initializers, and remove it after
>any member destructors but before any base class destructors, would be
>conforming.

 That is not quite correct. The dynamic
type has to be assigned _between_ the completion of base initialisation
and before the constructor of the first member is
called -- no sooner and no later. That means the only "flexibility"
is during the evaluation of the arguments to the constructor call
of a the first member to be initialised.

 The reason is obvious -- virtual dispatches invoked
dynamically in a base constructor had better NOT dispatch
past the type of the class whose constructor body is executing.

 That is, calling an impure virtual function should
always work (if the this pointer of the static type
can be calculated).
>
>8 Member functions (including virtual member functions, _class.virtual_)
>  can  be called for an object under construction.  Similarly, an object
>  under  construction  can  be  the  operand  of  the  typeid   operator
>  (_expr.typeid_)  or of a dynamic_cast (_expr.dynamic.cast_).  However,
>  if these operations are performed in a ctor-intializer (or in a  func
>  tion  called directly or indirectly from a ctor-intializer) before all
>  the mem-initializers for base classes have completed,  the  result  of
>  the operation is undefined.

 Exacty right.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: Wed, 22 Feb 1995 06:26:04 GMT
Raw View
In article <3i2i7d$o36@silver.jba.co.uk>,
Jonathan de Boyne Pollard <JdeBP@utopium.jba.co.uk> wrote:
>John Max Skaller (maxtal@Physics.usyd.edu.au) wrote:
>:  The answer is not as simple as you think, it is not
>: just a matter of "the" dynamic type. The dynamic type _changes_
>: during construction, and there is more than one entity
>: during construction that has a dynamic type. Separately,
>: being able to address components is an issue.
>
>I'm not sure that I follow that.  It seems to me that every instance has
>one dynamic type, and once assigned it never changes.  *Sub*-instances (i.e.
>instances of base classes) have a separate dynamic type.
>

 I look at it the other way. It is _only_ base subobjects that
have dynamic type. Actually, that's not right either. What is
correct is:

 A POINTER T* has a static type "T" and a dynamic type (maybe :-).
This applies to the "this" pointer as well.

 Do you follow? Objects never have dynamic type. An object
has a type. Thats it. During construction of a Derived, there is
no Derived object to have a type. When there is, it is always a
Derived.

 A base subobject is not an object. It can and does have
a dynamic type, which is the type its this "pointer" has.
That type can change during construction, it starts of as "void",
then changes to the class, then changes to each class derived
from the base until the most derived class is reached.

 Note this implies "switching" as well as subtyping.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 22 Feb 1995 15:54:24 -0000
Raw View
John Max Skaller (maxtal@Physics.usyd.edu.au) wrote:
: In article <3i2i7d$o36@silver.jba.co.uk>,
: Jonathan de Boyne Pollard <JdeBP@utopium.jba.co.uk> wrote:
: >I'm not sure that I follow that.  It seems to me that every instance has
: >one dynamic type, and once assigned it never changes.  *Sub*-instances (i.e.
: >instances of base classes) have a separate dynamic type.

:  I look at it the other way. It is _only_ base subobjects that
: have dynamic type. Actually, that's not right either. What is
: correct is:

:  A POINTER T* has a static type "T" and a dynamic type (maybe :-).
: This applies to the "this" pointer as well.

:  Do you follow? Objects never have dynamic type. An object
: has a type. Thats it. During construction of a Derived, there is
: no Derived object to have a type. When there is, it is always a
: Derived.

Yes.  That's the essence of my understanding too.  I believe that we are
both saying the same thing in different ways.  I wish that there were a
better phrase than "dynamic type" to represent the actualy type of an
instance at run-time as opposed to the compiler-known type of the instance.

"Dynamic" implies that it changes, when it really doesn't.  It's simply set
during the constructor, and reset during the destructor.

My original question was "when?", and this appears to have been answered.
Dynamic type information is guaranteed to have been set for every instance
after all base sub-objects have been initialised and before any data members
are initialised, and destroyed in the symmetrical place in the destructor.

This has the interesting consequence (by my reading), of making calls to
virtual functions when evaluating the arguments for base constructors
undefined (which reflects the in-the-field behaviour of two out of the four
compilers).

:  A base subobject is not an object.

This is obviously one of those hair-splitting what-does-the-word-"object"-
-actually-mean things.  (-:

What's worse, is that in DirectToSOM C++, classes themselves are objects
too ... (here be dragons).

As far as I can see, then, a base sub-instance doesn't have the same sort
of type information mechanism that an "ordinary" instance would have.  It
instead has a mechanism whereby the type information "points" to the type
information of the enclosing derived instance.

A further question, then, is "when is the type information of a sub-instance
set to `point' to the type of the derived instance, and when is it set back
again?".  It seems that the answer is that it is set to the derived type
in the same place as the type information for the enclosing derived
instance is itself created, and reset in the same place as that information
is destroyed.




Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 16 Feb 1995 11:55:13 -0000
Raw View
Steve Clamage (clamage@Eng.Sun.COM) wrote:
: JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:
: >It seems that this would be the sort of thing that would be implementation
: >defined, or possibly not be required to be defined at all.  We would be
: >guaranteed that the dynamic type of an instance would be correct from after
: >the completion of the constructor up until the start of the destructor.
: >But nothing that I can find will actually confirm this.

: The C++ standard when release will confirm this.

Confirm which ?  That it is implementation defined (so that one can rely on
certain semantics given the documentation for one's compiler), or that it is
not defined (so one's compiler isn't requried to document this) ?

: >Some experimentation leads me to believe that most implementations assign
: >dynamic type at the start of the constructor body after any member or base
: >class initialisers, and remove it at the end of the destructor body before
: >any member or base class destructors.
: >
: >However, this is not universally the case.  At least two of the C++
: >compilers in my possession (both of which are DirectToSOM C++ compilers)
: >assign the dynamic type to an instance before any member or base class
: >initialisers are invoked.

: During construction of a base class, the dynmamic type must be that
: of the base class, not the derived class.

Of the base class sub-instance, the dynamic type was indeed that of the base
class.  However, the dynamic type of the instance as a whole was that of
the derived class.  I'll post what I was doing to determine this in the
next message.

:                                            Perhaps the base-class
: constructor assigns the dynamic type upon entry, and the derived-class
: ctor reassigns it after the base-class ctor returns.

Ah.  No.  Lack of clarity on my part.  No such shenanighans appeared to be
occurring.  The dynamic type of the base class sub-object was indeed that
of the base class.  However, the dynamic type of the derived class instance
was that of the derived class.

See the next message for details.  I'd appreciate any comment.

: Perhaps SOM has special requirements. That force this implementation.

I'm not sure whether it has rigid specifications for dynamic type or not.
I'll have to check.  It probably does.




Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 16 Feb 1995 13:01:41 -0000
Raw View
Steve Clamage (clamage@Eng.Sun.COM) wrote:
: JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:
: >Some experimentation leads me to believe that most implementations assign
: >dynamic type at the start of the constructor body after any member or base
: >class initialisers, and remove it at the end of the destructor body before
: >any member or base class destructors.
: >
: >However, this is not universally the case.  At least two of the C++
: >compilers in my possession (both of which are DirectToSOM C++ compilers)
: >assign the dynamic type to an instance before any member or base class
: >initialisers are invoked.

: During construction of a base class, the dynmamic type must be that
: of the base class, not the derived class.

As it was.  Sorry for not being clear.

Here's my experimentation, as promised.

I was using a combination of a virtual function call and the DirectToSOM
C++ dynamic type information to see exactly what different implementations
did in this murky area of not-clearly-defined behaviour.  I've appended the
source of my test program to the end of this message.

With both Borland C++ and Watcom C++, the program compiled fine.  However,
at runtime the call to the virtual function in the base class initialiser
caused an exception (SYS3175) and halted the program.  Removing those calls
gave the following output :

    [e:\temp]b-dtype.exe
    constructor body of B calls B::report []
    constructor body of D calls D::report []
    usage of D calls D::report []
    destructor body of D calls D::report []
    destructor body of B calls B::report []

It looks like Borland C++ and Watcom C++ both assign dynamic type after any
base or member initialisers, and before the constructor body proper.

With IBM CSet++ using DirectToSOM C++, the program compiled fine and produced
the following output :

    [e:\temp]i-dtype.exe
    constructor body of B calls B::report [D->B]
    constructor body of D calls D::report [D]
    usage of D calls D::report [D]
    destructor body of D calls D::report [D]
    destructor body of B calls B::report [D->B]

It looks like IBM CSet++ takes the approach of disallowing such use in the
first place, because without the #if/#endif it failed to compile, yielding
the error "base class initialisers cannot contain virtual function calls".

With MetaWare High C++ using DirectToSOM C++, the program compiled fine and
produced the following output :

    [e:\temp]h-dtype.exe
    base-initialiser of D calls D::report [D]
    mem-initialiser of B calls D::report [D]
    constructor body of B calls B::report [D->B]
    constructor body of D calls D::report [D]
    usage of D calls D::report [D]
    destructor body of D calls D::report [D]
    destructor body of B calls B::report [D->B]

The virtual function calls made in the base initialiser and member
initialiser are the most interesting ones.  It appears that dynamic type
for an instance is already fully present even before base class
initialisers are called.

 ---------------------------------------------------------------------------

    #include <iostream.h>

    #if defined(__HIGHC__)
    #   include <som.hh>
 pragma Off(Case_insensitive_class_names) ;
    #elif defined(__IBMCPP__)
    #   include <som.hh>
    #else
    inline const char * somGetClassName() { return "" ; }
    class SOMObject {} ;
    #endif

    class B : public SOMObject {
    public:
    #if defined(__IBMCPP__)
 #pragma SOMClassName(*, "B")
    #endif
 int a ;
 B(int) ;
 ~B() ;
 virtual int report (const char *msg)
 {
     cout << msg << " calls B::report [" << somGetClassName()
                 << ']' << endl ;
     return 0 ;
 }
    } ;

    B::B(int) :
    #if defined(__HIGHC__) || defined(BORLAND_AND_WATCOM_DISLIKE_THIS)
 a(report("mem-initialiser of B"))
    #else
 a(0)
    #endif
    {
 report("constructor body of B") ;
    }
    B::~B()
    {
 report("destructor body of B") ;
    }

    class D : public B {
    public:
    #if defined(__IBMCPP__)
 #pragma SOMClassName(*, "D")
    #endif
 D() ;
 ~D() ;
 virtual int report (const char *msg)
 {
     cout << msg << " calls D::report [" << somGetClassName()
                 << ']' << endl ;
     return 1 ;
 }
    } ;

    D::D() :
    #if defined(__HIGHC__) || defined(BORLAND_AND_WATCOM_DISLIKE_THIS)
 B(report("base-initialiser of D"))
    #else
 B(0)
    #endif
    {
 report("constructor body of D") ;
    }
    D::~D()
    {
 report("destructor body of D") ;
    }

    int main ( int, char ** )
    {
 D d ;
 d.report("usage of D") ;
 return 0 ;
    }




Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 17 Feb 95 16:17:49 GMT
Raw View
John Max Skaller (maxtal@Physics.usyd.edu.au) wrote:
:  The answer is not as simple as you think, it is not
: just a matter of "the" dynamic type. The dynamic type _changes_
: during construction, and there is more than one entity
: during construction that has a dynamic type. Separately,
: being able to address components is an issue.

I'm not sure that I follow that.  It seems to me that every instance has
one dynamic type, and once assigned it never changes.  *Sub*-instances (i.e.
instances of base classes) have a separate dynamic type.

As far as I can understand it the problem is more one of where each dynamic
type comes into play (i.e. which dynamic type is referred to according to
the scope at the time of the virtual function call or RTTI enquiry), and
when it is assigned and removed.

Natty awful ASCII character diagram time, I think.  Here's what I see :

 .----.-----------------------------------------------------------------.
 | DT |        Instance of class Derived                                |
 `....'..../...........................................\................'
          /                                             \
         .----.------------------------------------------.
  | BT |    Sub-instance of class Base            |
         `----'------------------------------------------'

DT and BT are the magic internal stuff that the compiler uses for holding
dynamic type information, which affect virtual function call dispatch
and RTTI, and about the details of which (as a C++ user) I care not a whit.
So my problem is knowing when in my constructors and destructors I can rely
on DT and BT being well defined (i.e. not undefined).

: >It seems that this would be the sort of thing that would be implementation
: >defined, or possibly not be required to be defined at all.

:  No, the committee has a much tighter requirement.

:  Do you want all virtual function calls in constructors
: to fail?

Some of them do already.  See my other posts.  I was thinking that maybe
we'd only be guaranteed that they wouldn't fail in constructor bodies, and
that calling them as member or base class initialisers would be implementation
defined, or not required to be defined.

Two compilers bear this out by generating programs that die horribly if you
try to do this, and a third generates an error at compile time.

And on one compiler, it all works!

: >By the way, I couldn't determine whether this was properly a standards or
: >a language question.  Please followup as you think appropriate.

:  Standards question.

I've adjusted the followups, then.  (-:

: Text of current WP is your only accurate guide. (Sorry)

A pity that (being a humble programmer of limited means) I cannot afford
such a copy to consult.  From responses so far it appears to be the only
place where dynamic type in C++ is discussed at all.  )-:

( Courtesy of Stephen Clamage, I have part of it, though, and I'm still
  trying to digest that. )




Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 15 Feb 1995 16:10:31 GMT
Raw View
JdeBP@jba.co.uk (Jonathan de Boyne Pollard) writes:

>When is the dynamic type of an instance assigned and removed in its
>constructor and destructor ?  Most of the documentation available to me right
>at this moment is pretty vague on the subject.

Yes. C++ has lacked a formal object model.

>It seems that this would be the sort of thing that would be implementation
>defined, or possibly not be required to be defined at all.  We would be
>guaranteed that the dynamic type of an instance would be correct from after
>the completion of the constructor up until the start of the destructor.
>But nothing that I can find will actually confirm this.

The C++ standard when release will confirm this.

>Some experimentation leads me to believe that most implementations assign
>dynamic type at the start of the constructor body after any member or base
>class initialisers, and remove it at the end of the destructor body before
>any member or base class destructors.

That's typical, and is pretty much required by language semantics.
It doesn't contradict the above.

>However, this is not universally the case.  At least two of the C++
>compilers in my possession (both of which are DirectToSOM C++ compilers)
>assign the dynamic type to an instance before any member or base class
>initialisers are invoked.

During construction of a base class, the dynmamic type must be that
of the base class, not the derived class. Perhaps the base-class
constructor assigns the dynamic type upon entry, and the derived-class
ctor reassigns it after the base-class ctor returns. (Some compilers
have had inefficient methods of initializing classes, such as
needless copying of vtable pointers. This isn't a correctness issue,
as long as the dynamic type is always correct at a point where it
makes a difference. Perhaps SOM has special requirements. That force
this implementation.)

--
Steve Clamage, stephen.clamage@eng.sun.com