From: cox@cadence.com (Henry Cox)
Subject: Virtual base classes and mem initializers
Followup-To: comp.std.c++
Summary: Either I do not undertand or I do not like the way this is handled
Keywords: mem initializers, virtual base classes, multiple inheritance
Organization: Cadence Design Systems. Lowell MA
Date: Fri, 11 Dec 1992 16:57:54 GMT

(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