(ref.doc)maxtal 120495
Next clamage 180495
Prev: rmartin 040495
Up: Usenet
Date: Wed, 12 Apr 1995 04:04:27 GMT Note:
John Max Skaller.
Look: Suppose I have some lattice, and a class X
has a (possibly indirect) base J which is the "join" of the virtual base V.
Suppose X wants to initialise V. It can "grab" the join
unilaterally by merely declaring V as a virtual base:
this has absolutely no effect on the semantics of any
program in the current C++ language.
Having derived from V, X is now the join class.
There is no semantic distinction between a direct and
indirect virtual base: every class derived from V directly
or indirectly is potentially a join class. In particular,
the most derived class always _may_ initialise a virtual base.
Now there is a problem: often virtual bases
have default constructors. You can invoke a default constructor
of a base implicitly in a derived class.
In particular, whether or not an indirect
virtual base has a default constructor, it is hard to tell
in an constructor of a class derived directly or indirectly from a virtual
base which has no explicit constructor call for the virtual base,
whether this is intended as a call to the default constructor,
or as an _absence_ of a call -- on the assumption a "join" class
will do the initialisation.
It is _impossible_ in C++ to tell -- while compiling a
translation unit -- if a virtual base requiring explicit
initialisation is initialised by a constructor. Thats because
whether or not an explicit call is written -- and _which_
constructor is called and passed _which_ arguments --
is an implementation detail of the constructor and may
be coded in a different translation unit. Or not coded at all.
This makes any static (compile time) verifcation of correctness
of programs impossible.
Thats not on. And so the join idea cannot work in C++.
The best you can do is say that _every_ class must
initialise every virtual base. Given that, there is only
one possible coherent answer to the question:
"which class initialises a virtual base?"
and that answer must be
"the most derived class"
because it is the only obvious unique candidate. The only other possibility
is "the class which _first_ derives from the base in the lattice".
That should be rejected as too dependent on left/right ordering
in the DAG.
Summary: on algebraic grounds alone we can deduce that the C++
virtual base initialisation rule is the unique correct rule for
virtual base initialisation.
Anything else requires major changes to fundamental notions of
the C++ language.
BTW: this is a rationalisation not a formal proof. Its purpose
is really to demonstrate the fact that the C++ language rule
as it stands is not without justification.
The join idea has been considered before and we now know it
doesn't really work. This "fact" is useful because combined
with the "fact" that assignment doesn't work either,
leads one to the conclusion that virtual bases not only ought
to have exactly two constructors -- the default and copy constructors --
(Or one, the default constructor, if you want to stop copying)
but they also ought to have no non-trivial data, so the
compiler generated default constructor (and copy and assignment
methods) are in fact always semantically valid.
[If you may or may not want to stop copying/assignment, you
declare these operators "protected" to defer the decision
to the inevitable concrete class]
These conditions are easy to verify for abstract classes
with NO data members.
And _this_ suggests a view of virtual bases as the correct
way to present interfaces. Which suggests separation of
interface and implementation classes.
Which leads to mixins :-)
Which provides a solution to the original problem which works
_in_ the existing C++ language.
(Whew)
automatically generated by info2www version 1.2.2.8