[comp.std.c++] Disambiguating inherited pure virtuals with explicit qualifiers

cline@cheetah.ece.clarkson.edu (Marshall Cline) (08/22/90)

Suppose class A has a function f(), and class B also has a function f().
If C multiply inherits from A and B, then calling f() on a C is ambiguous.
Supposedly (ARM section 10.1.1) explicit qualification (ex: A::f) can be
used in C to disambiguate these f()'s.

But suppose A::f() is virtual and B::f() is pure virtual:
	class A { public: virtual int f();     };	//A concrete class
	class B { public: virtual int f() = 0; };	//An abstract class
	class C : public A, public B { public: A::f; };	//A concrete class???
(This is not as obscure as it looks -- see below for a real-life example!)

The rule for making a class non-abstract is that all pure virtuals need
to be `filled in.'  It seems reasonable that an explicit qualifier in C
such as `A::f' fully specifies the definition of C::f(), so C ought NOT
to be abstract.  Unfortunately this doesn't appear to be the case in
either g++ or Turbo-C++ (both seem to think that C is still abstract, as
well as giving a pre-2.1 complaint to either implement each pure virtual
or else re-declare them as pure virtual).

The only thing I've been able to do is fully implement C::f() as calling
B::f().  This gets old fast (especially when there are numerous member
functions to be re-implemented) since it also adds an extra (fortunately
non-virtual) function-call to each access.

Any light?  If this isn't already covered by the ARM (I didn't find it),
I propose that the following `reasonable' behavior be accepted as correct:

	When class C has multiple public base classes A and B, with A::f()
	being a (non-pure) virtual and B::f() being a pure virtual, an
	explict qualification in C such as `A::f;' means that f() is NOT
	a pure virtual in C.

Marshall Cline

PS: Here's the scenario that got me into this mess
(press 'n' if you're not interested...)

A common C++'ism (at least in my code!) is to hang several alternate
implementations of a `concept' from an abstract base class (ABC).  Ex:

                              Stack
                              (ABC)
                             /     \
                       VStack       LStack         etc etc
                  (Vector-based)   (List-based)

Now consider an `Invertable Stack' as a specialized Stack, and suppose
(rather than retrofitting an `invert()' member into each Stack class)
you want a separate hierarchy.  That's easy: create an InvStack ABC which
inherits from the Stack ABC.  There's no reason to prevent someone from
implementing VInvStack (the vector based InvStack) by inheriting from
VStack, which leads to multiple inheritance:

                   Stack
                   (ABC)
                     |  \________
                     |           \
                     |          InvStack
                     |           (ABC)
                   VStack          |
                        \______    |
                               \   |
                               VInvStack

I don't want to argue whether you *need* MI in this example -- clearly
VInvStack can *contain* a VStack (at the cost of reimplementing all the
member functions), or could *privately* derive from VStack (at the cost
of specifying the access of all VStack member functions as public).  I
just think the above is a natural and convenient strategy, since reusing
the VStack implementation means you only have to implement the
orthogonally added function `invert()'.

Furthermore note that `push()', `pop()', etc, will be pure virtual in
InvStack simply by reason of the fact that they were inherited as pure
virtual from Stack (this is a new rule in 2.1).  *However* VInvStack will
also inherit the *implemented* `push()', `pop()', etc, from VStack.

QUESTION: How can I convince the compiler to use VStack's implementation
of `push()'??

Neither GNU C++ nor Turbo-C++ accept explicit qualification (ex:
`VStack::push;') in the public section of VInvStack (when I try to
instantiate a VInvStack, both complain that I never gave an
implementation of a pure virtual).  The only thing I've been able to do
is re-implement each member function so it calls the VStack version.
This is unacceptable since it not only costs me implementation (and
maintenance) time, it also costs an extra (fortunately non-virtual)
function call for each access.
--
==============================================================================
Marshall Cline / Asst.Prof / ECE Dept / Clarkson Univ / Potsdam, NY 13676
cline@sun.soe.clarkson.edu / Bitnet:BH0W@CLUTX / uunet!clutx.clarkson.edu!bh0w
Voice: 315-268-3868 / Secretary: 315-268-6511 / FAX: 315-268-7600
Career search in progress; ECE faculty; research oriented; will send vita.
PS: If your company is interested in on-site C++/OOD training, drop me a line!
==============================================================================

mjv@objects.mv.com (Michael J. Vilot) (08/24/90)

Marshall Cline describes a problem with multiple inheritance:
> an explicit qualification ... means that f() is NOT a pure virtual
This is actually a specific instance of a more general problem.

Section 10.11c in E&S mentions the topic of ``Renaming'' and the problem of
finding an appropriate way to express the semantics of merging names from
multiple base classes. The status of inherited virtuals is similar.

Bjarne's current proposal for ``Overriding'' (the topic's been Renamed ;-)
addresses these concerns.

> A common C++'ism
One more vote to make it ``common'' -- this is exactly the approach we used
in The C++ Booch Components library.  Our paper at the ECOOPSLA conference
presents the design.

I'm looking forward to next month's presentation of Annotated C++, I think
it's important work.

--
Mike Vilot,  ObjectWare Inc, Nashua NH
mjv@objects.mv.com  (UUCP:  ...!decvax!zinn!objects!mjv)