Topic: Virtual base classes and mem initializers


Author: cox@cadence.com (Henry Cox)
Date: Fri, 11 Dec 1992 16:57:54 GMT
Raw View
(I posted this last night - it did not appear on our systems, so I am
posting it a second time.  I apologize for any duplication you see.)

I ran into an interesting situation with respect to multiple
inheritance and virtual base classes.  The following example
illustrates the problem:

class A {
  A(int) { /*... */ ; }
};

class B : virtual public A {
  B(int a, int b) : A(a) { /* ... */ ; }
};


class C : virtual public A {
  C(int a, int c) : A(a) { /* ... */ ; }
};


class D : public B, public C {
  D(int a, int b, int c, int d) : A(a), B(a,b), C(a,c) { /* ... */; }
}


class E : public D {
#ifdef WIBNI /* [1] */
     // what I would LIKE to be able to do
  E(int a, int b, int c, int d, int e) : D(a,b,c,d) { /* ... */; }
#else
     // what I actually HAVE to do
  E(int a, int b, int c, int d, int e) : A(a),D(a,b,c,d) { /* ... */; }
#endif
}

/* [1] Wouldn't It Be Nice If */

The English explanation:  My inheritance graph is as follows:

            A
    / \
          B   C
    \ /
     D
     |
     E

"A" is a virtual base class of "B" and "C".  "A" does not have a
default constructor (my application is a bit more involved than the
above).

"B" and "C" are base classes of "D".  I understand and support that
"D"'s constructor(s) must specify how the virtual base class "A" is
constructed.  (If this was not the case, there could be ambiguities in
how "A" was actually constructed, given that the calls to "A"'s
constructor from "B" and from "C" could be different.)  This is
explained in the ARM S12.6.2, pp. 292--4.

What I don't understand is why "E" must also declare how the virtual
base class "A" is to be constructed - shouldn't the compiler be able
to figure out that the "virtualness" of "A" at "E" comes about at "D",
and thus do the right thing:
 1) try to construct a "D", the base class of "E"
 2) note that "A" is a multiply-inherited virtual base class of
    "D" - and thus that "A"'s constructor must be called with
    the arguments as specified by "D"'s constructor ("A"'s
    default constructor if none is declared).
 3) construct "A"
 4) try to construct "B" - the first base class of "D"
 5) note that "A" is a virtual base class of "B" - note that
    "A" has already been constructed - "B"'s "A" is done -
    ignore)
 6) execute "B"'s constructor
 7) try to construct "C" - the second base class of "D"
 5) note that "A" is a virtual base class of "C" - note that
    "A" has already been constructed - "C"'s "A" is done -
    ignore)
 6) execute "C"'s constructor
 7) execute "D"'s constructor
 8) execute "E"'s constructor

I believe that the implemention (gcc 2.3.2 and sun C++ vers. 2.1) is
bad in that anyone who uses class D, or any class derived from D, must
know that _complete_ inheritance graph of their class in order to know
which virtual base class constructors they must call.  (It is not
sufficient that my code compiles, as, for example, default
constructors may be called when I really want a different one...I just
don't know that I want a different constructor until I understand the
entire inheritance graph.)  If nothing else, this violates the
object-oriented paradigm - at least as I understand it.


What I propose, then is that the virtual base class constructor call
be required _only_ at the apex of a multiple-inheritance cone.
Otherwise, the compiler should figure it out, from the base classes,
exactly as for a "normal" base class.


For example

class F : public E, public C {
   F(...) : A(...), E(...) { /*... */;}
     /* A's constructor must be named, as F is
        at the apex of an inheritance cone - it
        contains only one copy of A, and I must
        declare how that A is to be initialized */
};


class G: public F, public A {
  G(...} : A(...), F(...) { /* ... */; }
     /* G contains _two_ copies of A - one virtually,
        from F, and one immediate (non-virtual).  A's
        constructor is called to construct the immediate
        one - the virtual one is constructed through F. */
};


Comments?  Suggestions?

I would appreciate it if you would send replies via email in addition
to posting them.  (Usually, I cannot keep up with the volume of these
groups.)

I will summarize and post any responses I receive.

Thanks.
--

     Henry




Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Sat, 12 Dec 1992 15:14:20 GMT
Raw View
In article <1992Dec11.165754.9910@Cadence.COM> cox@cadence.com (Henry Cox) writes:
>(I posted this last night - it did not appear on our systems, so I am
>posting it a second time.  I apologize for any duplication you see.)
>
>I ran into an interesting situation with respect to multiple
>inheritance and [initialising] virtual base classes.

>Comments?  Suggestions?
>
 Markku Sakkinen has written a paper on this issue, due to
appear in the C++ Report I believe. it is generally felt
that initialisation by the 'join' class should be used.
 Another idea I put forward was to use the name dominance
rule, but this would require the ctor initialisers to be part
of the interface.

 The real question is 'why am I initialising virtual bases',
and 'how do I explain that there are *different* initialisers'.
It should be that if a base is initialised at all, it is initialised
in only one way. This is enforced for single inheritance by
only allowing initialisation of direct bases, and for virtual bases
by insisting that the most derived class initialises them,
even if not direct bases, so any other initialisers are unfortunately
ignored.

 This phenomenon is extremely annoying when using mixins,
when all the abstract base classes are virtual, and the whole lot
have to be initialised in the most derived class.

 A general rule of thumb would appear to be that candidates
for shared base status should never require non-default initialisation,
since this is the only reasonable way to have unique initialisation.

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: jimad@microsoft.com (Jim Adcock)
Date: 14 Dec 92 21:31:14 GMT
Raw View
In article <1992Dec11.165754.9910@Cadence.COM> cox@cadence.com (Henry Cox) writes:
|What I propose, then is that the virtual base class constructor call
|be required _only_ at the apex of a multiple-inheritance cone.
|Otherwise, the compiler should figure it out, from the base classes,
|exactly as for a "normal" base class.
|
|Comments?  Suggestions?

1) Don't use virtual base classes.

2) If you insist on using virtual base classes, use default constructors.