From: jimad@microsoft.com (Jim Adcock)
Subject: Re: Must derived class reserve space for an empty base class?
Date: 24 Dec 92 00:36:04 GMT
Organization: Microsoft Corporation

In article <5450@holden.lulea.trab.se> jbn@lulea.trab.se (Johan Bengtsson) writes:
|Jim Adcock (jimad@microsoft.com) wrote:
|: In article <5386@holden.lulea.trab.se> jbn@lulea.trab.se (Johan Bengtsson) writes:
|: |That two non-generic pointers of the same type (possibly converted from
|: |pointer-to-derived) to different objects or data members should be
|: |guaranteed to compare unequal.
|
|Your example (below) shows that at least we have established what we
|are disagreeing on.

No, it merely establishes one thing that we are disagreeing on.

|But the cases where "unnecessary" padding would be required would
|have very minor impact on the majority of programs, at least if I
|may consider the current implementation of virtuals (vtbl).  After all,
|inheriting from a truly empty base class (no virtuals) is quite uncommon,
|don't you think?

No.  Since the use of vtables and vtptrs is an implementation decision,
not a language decision, classes are "empty" if they have no member 
objects, regardless of whether or not these classes include pure 
virtual functions.  There is no requirement that implementatins put
vtptrs where you are assuming they go.  There is no requirement that
implementations even *have* vtptrs.

Given that the vtptr issue is an implementation decision, I'd say on
the contrary "empty" base classes are quite common.  As in pure virtual
base classes.

|If pointer distinctness (as defined above) allows useful semantic constructs,
|such as the very common idiom for operator=:
|"if ( &other == this ) return", then a small and rare storage overhead
|in examples such as the one below could easily be tolerated (IMHO).

Other people on the net have complained loudly, repeatedly, and
correctly [IMHO] about implementations that consume space when
inheriting from a pure virtual base classes.

|: If you want object addresses to be
|: unique, then write your code so that they are unique.
|
|You cannot always guarantee that.  Suppose you create a class template
|that uses pointers to an unknown class (T*) for identity tests (the
|template could be specified to handle only statically allocated objects,
|if you like).  That template cannot know that the type parameter (T)
|specifies a class that will have distinct pointers to all instances,
|_unless_ pointer distinctness is universally guaranteed.

Again you are trying to insist that your template class works for
every and all target types which is pure and simple nonsense.
Templates can and will always only work on target types that implement
not only the right syntax, but also the right semantics.  THE ONLY WAY
a template can know that the target type supports the right semantics
is for the template writer to SPECIFY the semantics, and for the template
invoker to CORRECTLY match such.  Otherwise all bets are off, even
if the syntax happens to match and the template compiles without 
compile-time error.  Again, this is nothing new, and has nothing to
do with pointers.  If the template writer expects certain correspondences
between pointers and object identities, the template writer should specify
such, and the template invoker must correctly implement such for the
target type.  Again, this is nothing new.

|There are existing commercial class templates that rely on pointer
|distinctness.  Are they invalid?

Of course they're "valid" -- they're simply not strictly portable.
They meet the needs of a portion of the marketplace, and do not
meet the needs of others.  If they want to meet the needs of a greater
portion of the marketplace, then they need to write their classes
to rely on even fewer implementation dependences.

In practice "portability" is never an absolute, its only a relative
term.  In practice, no interesting program or library is "strictly
portable".  Interesting programs or libraries only have sections of
code that can be informally referred to as "portable" and other
sections of code that are clearly not strictly portable.  By
making things more portable the implementor makes porting the code
less expensive, while typically raising BOTH the cost of implementing
that code AND the cost of executing that code.   While we tend, as
a rule of thumb, think of more portable code as being better, in
reality such comparisons are only relatively true.