Topic: generalized overriding proposal
Author: bs@alice.att.com (Bjarne Stroustrup)
Date: 28 Oct 90 00:50:38 GMT Raw View
This note is in response to a message sent to me by Kjell Eriksson
(erialfa!kjelle@data.nokia.se). He cannot be the only person
interested and I apologize for not posting a summary earlier:
I got the C++ report for October today. It mentions
a proposal for overriding. What? When? Why??
Could you point me towards some reference or better
still perhaps write a short article (very short) for
comp.std.c++?
The issue is described under the title ``renaming'' on page 236 of the ARM:
******
Merging two class hierarchies by using them as bases classes
for a common derived class can cause a practical problem where
the same name is used in both hierarchies, but where it refers
to different operations in the different hierarchies.
For example,
class Lottery {
// ...
virtual int draw();
};
class GraphicalObject {
// ...
virtual void draw();
};
class LotterySimulation
: public Lottery ,
public GraphicalObject {
// ...
};
In LotterySimulation we would like to override both Lottery::draw()
and GraphicalObject::draw(), but with two distinct functions,
since draw() has completely different meanings in the two base
classes. We would also like LotterySimulation to have distinct,
unambiguous names for the inherited functions Lottery::draw() and
GraphicalObject::draw(). The semantics of this concept are simple,
and the implementation is trivial; the problem seems to be to find
a suitable syntax. The following has been suggested:
class LotterySimulation
: public Lottery ,
public GraphicalObject {
// ...
virtual int l_draw() = Lottery::draw;
virtual void go_draw() = GraphicalObject::draw;
};
This would extend the pure virtual syntax in a natural manner.
It could be considered a weakness that the notation applies only
to virtual functions. Alternatively, this can be considered the
real beauty of the scheme.
******
As you see the basic solution is also suggested there. In the extensions
working group of X3J16 (The ANSI C++ standards group) we have waded through
many details and alternative schemes that I'd prefer not to go into here
(I'm short on time as ever). Maybe, if this summary is considered too brief,
other members of the working group can come to my rescue.
One question seems key to the solutions: Should a name that has been overridden
by another name still be visible? We more or less concluded that the answer
must be YES. This is irrelevant to the example above since a simple reference
to draw() for Lotterysimulation would be ambiguous anyway, but it is relevant
for names overriden in a single inheritance hierarchy and for overloaded names.
I - and apparently most C++ programmers - considered the overriding problem
unsolvable in C++ as defined. Since the problem is real and potentially nasty
this would imply that some language extension - like the one suggested in the
ARM - is necessary. However the problem do have a solution:
class L2: public Lottery {
virtual int l_draw() = 0;
int draw() { return l_draw(); } // overrides Lottery::draw
};
class G2 : public GraphicalObject {
virtual void go_draw() = 0;
void draw() { go_draw(); } // overrides GraphicalObject::draw
};
class LotterySimulation : public L2 , public G2 {
// ...
int l_draw();
void go_draw();
};
That is, we use a forwarding function for each function we need a
new name for. Overriding the original function with a function that
simply calls the new function ensures that an LotterySimulation can
be treated as, say, a Lottery and a call of draw() will correctly call
an l_draw() defined by a LotterySimulation or a class derived from
LotterySimulation.
This technique is so simple - and in retrospect so obvious - that it
must have been independently invented many times but in this context
the credit goes to Doug McIlroy.
This implies that we must argue for a language extension based on elegance,
efficiency, or convenience; NOT on necessity.
I currently have a weak preference for making no change. This preference is
based on the assumption that such renaming of a function is not something a
programmer will need to do every week.
As all interesting techniques generalized overriding has uses beyond solving
the problem it was invented to solve. Part of the big question is to which
extent these ``additional'' uses makes the language complexity implied by
generalizing overriding worth while. In other words: Are we seeing an
example of creeping featurism or an example of useful language evolution?
Here are two examples of uses in addition to ``renaming:''
Name Merging:
Consider
class B1 {
public:
virtual int print();
};
class B2 {
public:
virtual int output();
};
Here we have two different named functions intended to perform
the same service. To avoid having to maintain two separate names
for this output operation in a class derived from both B1 and
B2 we need a mechanism to say that a function overrides both:
class D : public B1, public B2 {
public:
virtual int draw() = { B1::print, B2::output };
};
Static Overriding Checking:
Note that given
class B {
// ...
virtual int f();
};
the degenerate form of generalized overnaming where f is explicitly
overridden by f
class D : public B {
public:
virtual int f() = B::f;
};
is legal and is identical to the ``plain'' version
class D : public B {
public:
int f();
};
wherever there really is a B::f of the right type to override.
If the name of the function being overridden does not refer to
a virtual function of the exact type of the overriding function
a compiler time error results. For example:
class DD : public B {
public:
virtual int f(int) = B::f; // error: there is no B::f(int)
};