Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Types and inheritance

1 view
Skip to first unread message

Tobias Schuele

unread,
Feb 22, 2001, 11:09:01 PM2/22/01
to
Hi,

I encountered the following problem with types in inherited classes:

Suppose there is a class N which represents natural numbers:

class N {
public:
N operator+(const N&);
...
protected:
int i;
}

>From this we derive a class Z for integers:

class Z: public N {
public:
Z operator-(const Z&);
...
}

Note that Z does not have any additional data members but only provides
some new methods. Unfortunately, the following piece of code does not
work:

Z i, j, k, l;
i = j + k - l;

Obviously, j and k are converted to objects of class N and thus do not
have an operator-(). One solution to this problem would be to fully
reimplement operator+ in Z or to reimplement it using a static_cast.
Both methods do not seem to be good style for me. Another solution is to
introduce an operator/constructor for the type conversion from N to Z.
However, this causes the elements to be copied which is not what I want.
Any other ideas, e.g. using templates? Sorry, if this is a newbie
question but I haven't found a satisfactory solution yet. Thank you very
much in advance for your help.

Best regards
Tobias

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Maciej Sobczak

unread,
Feb 23, 2001, 12:38:08 PM2/23/01
to
Hi,

> Z i, j, k, l;
> i = j + k - l;
>
> Obviously, j and k are converted to objects of class N and thus do not
> have an operator-().

To be exact, they are not converted. operator+ defined in class N accepts
REFERENCES, so no conversion is made from the derived type to the base one.
But the return value from this operator is of type N and appears ON THE LEFT
SIDE of the operator-. Thus, the compiler will look for
1. member operator- in a class N, which could accept Z (and do not find it)
2. global operator- which could accept N on the left and Z on the right (and
do not find it also).

If you want to overcome it, you could:
1. add new operator- as a member to the class N. It will allow you to
substract Ns with the result type Z:
Z operator-(const N&);
2. add a global operator-. This allows you to decide, what type you have on
the left side of the binary operators.

I suppose the latter (2) approach is much better.
The choice between member and non-member binary operators is an endless
dilemma, but in this case I choose a global one:
Z operator-(const N &a, const N &b)
{
//...
}

Regards,
Maciej Sobczak, http://www.cern.ch/Maciej.Sobczak

Michiel Salters

unread,
Feb 23, 2001, 5:27:24 PM2/23/01
to
Tobias Schuele wrote:

> Hi,
> I encountered the following problem with types in inherited classes:

> Suppose there is a class N which represents natural numbers:

> class N {
> public:
> N operator+(const N&);
> ...
> protected:
> int i;
> }

> >From this we derive a class Z for integers:

> class Z: public N {
> public:
> Z operator-(const Z&);
> ...
> }

Thus Z is a proper superset of N.

> Note that Z does not have any additional data members but only provides
> some new methods. Unfortunately, the following piece of code does not
> work:

> Z i, j, k, l;
> i = j + k - l;


> Obviously, j and k are converted to objects of class N and thus do not
> have an operator-(). One solution to this problem would be to fully
> reimplement operator+ in Z or to reimplement it using a static_cast.

Well, yes, that's a problem. Perhaps you shouldn't have had N::operator+(const
N&)
but operator+(N , N ) instead. And operator-(Z,Z) as well.

> Both methods do not seem to be good style for me. Another solution is to
> introduce an operator/constructor for the type conversion from N to Z.
> However, this causes the elements to be copied which is not what I want.
> Any other ideas, e.g. using templates? Sorry, if this is a newbie
> question but I haven't found a satisfactory solution yet. Thank you very
> much in advance for your help.

Quoting Kent Beck: "Get it right, then get it fast".

Of course you should provide Z::Z(N). Together with operator-(Z,Z) that
will make your method work, and your compiler will eliminate copies of
objects for you. Worrying about copies before you have at least one working
solution is indeed a newbie problem. Experienced C++ programmers look
at (possible) unneeded copies only when to choose from multiple
solutions. Really experienced programmers try to delay that choice until
profiling shows the copying is the bottleneck for the program.

> Best regards
> Tobias

HTH,
Michiel Salters
--
Michiel Salters
Michiel...@cmg.nl
sal...@lucent.com

Francis Glassborow

unread,
Feb 23, 2001, 6:46:05 PM2/23/01
to
In article <3A952E2A...@ira.uka.de>, Tobias Schuele
<sch...@ira.uka.de> writes

>Hi,
>
>I encountered the following problem with types in inherited classes:
>
>Suppose there is a class N which represents natural numbers:
>
>class N {
>public:
> N operator+(const N&);

Did you truly mean to provide this as an in class function?
> ...
>protected:
> int i;

ugh! and having said that the class is designed for inheritance, where
is the virtual dtor


>}
>
>>From this we derive a class Z for integers:
>
>class Z: public N {
>public:
> Z operator-(const Z&);

And now you will suffer all the consequences. Why are you doing this? Do
you really need N to be a type that does not support subtraction? And
why the Z const & rather than N const &?

> ...
>}
>
>Note that Z does not have any additional data members but only provides
>some new methods. Unfortunately, the following piece of code does not
>work:
>
>Z i, j, k, l;
>i = j + k - l;

And designed the way you have written it, it is unlikely to do so. If
you elected to write
Z & Z::operator -= (N const &)
as a member of Z, you could write:

Z operator-(N const & n, Z const & z){
return (N(n)+=z);
}

as a free function.

But I think the whole design is flawed, and unless you can explain why
you what you are trying to do, and why you are trying to do it, there
are no reasonable answers.

--
Francis Glassborow
See http://www.accu.org for details of The ACCU Spring Conference, 2001
(includes many regular participants to C & C++ newsgroups)

Siemel Naran

unread,
Feb 24, 2001, 6:24:12 AM2/24/01
to
"Tobias Schuele" <sch...@ira.uka.de> wrote in message

> I encountered the following problem with types in inherited classes:
>
> Suppose there is a class N which represents natural numbers:
>
> class N {
> public:
> N operator+(const N&);
> ...
> protected:
> int i;
> }

Why isn't operator+ a non-member?


> >From this we derive a class Z for integers:
>
> class Z: public N {
> public:
> Z operator-(const Z&);
> ...
> }

I don't think derivation is a good idea. You may have to end up using
virtual check functions, and this would slow your class down. Instead, make
class N and Z indendent, and provide conversion constructors or functions to
convert from N to Z.

This approach may even avoid the problem you seek to solve.


--
+++++++++++
Siemel Naran

Andrei Alexandrescu

unread,
Feb 24, 2001, 7:06:50 AM2/24/01
to
"Michiel Salters" <sal...@lucent.com> wrote in message
news:3A961A1A...@lucent.com...

> > >From this we derive a class Z for integers:
>
> > class Z: public N {
> > public:
> > Z operator-(const Z&);
> > ...
> > }
>
> Thus Z is a proper superset of N.

Your whole post addresses some technical points that hide the main point: Z
is a superset of N, but this relationship cannot be expressed by
subclassing.


Andrei

Frank Muzzulini

unread,
Feb 24, 2001, 5:23:03 PM2/24/01
to
Tobias Schuele <sch...@ira.uka.de> writes:
> Note that Z does not have any additional data members but only provides
> some new methods. Unfortunately, the following piece of code does not
> work:
>
> Z i, j, k, l;
> i = j + k - l;
>
> Obviously, j and k are converted to objects of class N and thus do not
> have an operator-(). One solution to this problem would be to fully
> reimplement operator+ in Z or to reimplement it using a static_cast.
> Both methods do not seem to be good style for me. Another solution is to
> introduce an operator/constructor for the type conversion from N to Z.
> However, this causes the elements to be copied which is not what I want.
> Any other ideas, e.g. using templates? Sorry, if this is a newbie
> question but I haven't found a satisfactory solution yet. Thank you very
> much in advance for your help.

Your design is wrong from the start, since Z is a superset of N and
not vice versa. That means a natural number is an integer and if want
to model this with inheritance, N should be derived from Z.

Now if you do this, you can implement all operators in class Z and all
class N should add is a constructor (and an operator=) that takes a Z
and checks that it is possitive.

What remains is the question what you actually plan to do, but I guess
this is no more than a practice, so you would be all right with the
above. To go on, try to write Z and N in way that N must not be
changed after you change Z to be derived from yet another class and
add a class Q. Happy hacking!

Muzz

--
___ Frank Muzzulini
<*,*> ... until the colour of a man skin is of no more
[`-'] significance than the colour of his eyes ... Haile Selassie
-"-"-

Michiel Salters

unread,
Feb 26, 2001, 8:32:05 PM2/26/01
to
Andrei Alexandrescu wrote:

> "Michiel Salters" <sal...@lucent.com> wrote in message
> news:3A961A1A...@lucent.com...
> > > >From this we derive a class Z for integers:

> > > class Z: public N {
> > > public:
> > > Z operator-(const Z&);
> > > ...
> > > }

> > Thus Z is a proper superset of N.

> Your whole post addresses some technical points that hide the main point: Z
> is a superset of N, but this relationship cannot be expressed by
> subclassing.
> Andrei

You're right. I should have stopped there.

In case it's not clear to the OP: Using (public) inheritance states that
that the derived class can be used where the base class can be used.
This is known as the Liskov Substitution Principle (LSP)

Supersets usually don't meet this requirement. For instance, the function
n!=n*(n-1)*(n-2)*...*2*1 in C++ has the interface N f(N n). The LSP
states that if any Z is-a N it must be acceptable to f. Clearly this
is not the case; f won't take -1.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Frank Muzzulini

unread,
Mar 1, 2001, 1:39:55 PM3/1/01
to
James Kanze <James...@dresdner-bank.com> writes:
> Right. Inheritance can be used for many different reasons, but in
> this case, I can't find any that would fit. About the only
> relationship I can see is that all elements of N are also elements of
> Z -- this suggests that an N can be converted into a Z, but no more.
> So you have a constructor Z::Z(N), and that's all.

There may be use for a conversion form Z to N as well (with an assert
of course). Just take operator-(N, N): What should it return? To be of
general use, Z would be first answer. However there may be cases in
which we know well that some N is no smaller than an other and want to
assign there difference a N (Euclid's algorithm to compute a GCD comes
to my mind). If we allow both conversions, operator- may simply return
a Z and we can still assign it to a N.

Making two classes 'exchangable' is a thing I would avoid in most
programs, but sometimes convenience is more important than many other
things and I would give it a try.

Muzz

--
___ Frank Muzzulini
<*,*> ... until the colour of a man skin is of no more
[`-'] significance than the colour of his eyes ... Haile Selassie
-"-"-

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bill Weston

unread,
Mar 1, 2001, 7:13:28 PM3/1/01
to
Frank Muzzulini wrote:

> James Kanze <James...@dresdner-bank.com> writes:
> > Right. Inheritance can be used for many different reasons, but in
> > this case, I can't find any that would fit. About the only
> > relationship I can see is that all elements of N are also elements of
> > Z -- this suggests that an N can be converted into a Z, but no more.
> > So you have a constructor Z::Z(N), and that's all.
>
> There may be use for a conversion form Z to N as well (with an assert
> of course). Just take operator-(N, N): What should it return? To be of
> general use, Z would be first answer. However there may be cases in
> which we know well that some N is no smaller than an other and want to
> assign there difference a N (Euclid's algorithm to compute a GCD comes
> to my mind). If we allow both conversions, operator- may simply return
> a Z and we can still assign it to a N.
>
> Making two classes 'exchangable' is a thing I would avoid in most
> programs, but sometimes convenience is more important than many other
> things and I would give it a try.
>
> Muzz
>
> --

Sorry, I'm being a bit daft here, but what is wrong with pubicly deriving
N from Z? Then sure you can initialize a Z with an N. But then I don't
see much wrong with deriving Square from base class Rectangle.
So what? A square maybe has an extra data member or two, and a N too, but
the interface is right.
I know that mathmos construct Z as equivalence classes over pairs of N's,
and you *could* go down that constructive avenue in c++, I guess, but
just between friends a natural number is an integer, right?
Come to think of it, using that representation, say a pair of unsigned
longs, an N needs just as much room as a Z. What's the problem? Z
operator-(const Z& lhs, const Z& rhs) still works when the arguments are
N's -- it still only promises to return a Z.

confused,
Bill.

James Dennett

unread,
Mar 1, 2001, 11:41:40 PM3/1/01
to
Bill Weston wrote:
>
> Frank Muzzulini wrote:
>
> > James Kanze <James...@dresdner-bank.com> writes:
> > > Right. Inheritance can be used for many different reasons, but in
> > > this case, I can't find any that would fit. About the only
> > > relationship I can see is that all elements of N are also elements of
> > > Z -- this suggests that an N can be converted into a Z, but no more.
> > > So you have a constructor Z::Z(N), and that's all.
> >
> > There may be use for a conversion form Z to N as well (with an assert
> > of course). Just take operator-(N, N): What should it return? To be of
> > general use, Z would be first answer. However there may be cases in
> > which we know well that some N is no smaller than an other and want to
> > assign there difference a N (Euclid's algorithm to compute a GCD comes
> > to my mind). If we allow both conversions, operator- may simply return
> > a Z and we can still assign it to a N.
> >
> > Making two classes 'exchangable' is a thing I would avoid in most
> > programs, but sometimes convenience is more important than many other
> > things and I would give it a try.
> >
> > Muzz
> >
> > --
>
> Sorry, I'm being a bit daft here, but what is wrong with pubicly deriving
> N from Z? Then sure you can initialize a Z with an N.

What if Z supports operator -- (as it should)? N can't support that.

> But then I don't
> see much wrong with deriving Square from base class Rectangle.
> So what? A square maybe has an extra data member or two, and a N too, but
> the interface is right.

Possibly, possibly not. What if the rectange include stretchX methods?

> I know that mathmos construct Z as equivalence classes over pairs of N's,
> and you *could* go down that constructive avenue in c++, I guess, but
> just between friends a natural number is an integer, right?

Yes, but the mutating operations on N are a subset of those on Z.

-- James Dennett <jden...@acm.org>

John J. Rusnak

unread,
Mar 2, 2001, 4:56:49 AM3/2/01
to
Bill Weston wrote:

>
> Sorry, I'm being a bit daft here, but what is wrong with pubicly deriving
> N from Z? Then sure you can initialize a Z with an N. But then I don't
> see much wrong with deriving Square from base class Rectangle.
> So what? A square maybe has an extra data member or two, and a N too, but
> the interface is right.
> I know that mathmos construct Z as equivalence classes over pairs of N's,
> and you *could* go down that constructive avenue in c++, I guess, but
> just between friends a natural number is an integer, right?
> Come to think of it, using that representation, say a pair of unsigned
> longs, an N needs just as much room as a Z. What's the problem? Z
> operator-(const Z& lhs, const Z& rhs) still works when the arguments are
> N's -- it still only promises to return a Z.

Is a natural number an integer? Every integer has an arithmetic opposite.
If a natural number is an integer, then it must also have an arithmetic
opposite. A class is more than an encapsulation of member elements, it is
also an interface for manipulting those elements. Suppose the class
Rectangle provides a method

virtual set_size( const unsigned int length, const unsigned int height);

Then every class which truly inherits from Rectangle must either inherit
this method, or faithfully provide its own implementaion to override it. I
don't see how a square class can uphold the contract set forth by the
Rectangle class without doing something drastic like (gasp!) raising an
execption when length and height are not equal. This is poor OO design.

In all honesty, it is a matter of how one defines the Rectangle class. It is
possible to define such a class in which the length and width are fixed upon
instantiaion:

class Rectangle
{
public:
Rectangle( const unsigned int a_width, const unsigned int a_height):
width( a_width),
height(a_height)
{
}

const unsigned int area();
const unsigned int perimeter();

private:
const unsigned int width, height;
};


Then I can see inheriting a Square class from this:

class Square: public Rectangle
{
public:
Square( const unsigned int width ):
Rectangle( width, width)
{
}
};

-John

David Casperson

unread,
Mar 2, 2001, 5:13:44 AM3/2/01
to
>Tobias Schuele <sch...@ira.uka.de> writes:
>> Note that Z does not have any additional data members but only provides
>> some new methods. Unfortunately, the following piece of code does not
>> work:
>>
>> Z i, j, k, l;
>> i = j + k - l;
>>
>> Obviously, j and k are converted to objects of class N and thus do not
>> have an operator-(). One solution to this problem would be to fully
>> reimplement operator+ in Z or to reimplement it using a static_cast.
>> Both methods do not seem to be good style for me. Another solution is to
>> introduce an operator/constructor for the type conversion from N to Z.
>> However, this causes the elements to be copied which is not what I want.
>> Any other ideas, e.g. using templates? Sorry, if this is a newbie
>> question but I haven't found a satisfactory solution yet. Thank you very
>> much in advance for your help.

In article <m2wvafg...@albatross.pond.sub.org>,
Frank Muzzulini <mu...@albatross.pond.sub.org> replies:


>
>Your design is wrong from the start, since Z is a superset of N and
>not vice versa. That means a natural number is an integer and if want
>to model this with inheritance, N should be derived from Z.

It's worth noting that in general binary methods (such as binary
arithmetic operators) do not work well with inheritance hieararchies.
There is survey paper by Luca Cardelli and others on this. If you are
willing to wade through the distinctions between sub-typing and
sub-classing it's worth reading. This cat's been skinned before, but
never cleanly.

David
--
Dr. David Casperson | cas...@unbc.ca
Department of Mathematics & Computer Science | (250) 960-6672
Faculty of Science |
College of Science and Management |

James Kanze

unread,
Mar 2, 2001, 12:12:27 PM3/2/01
to
James Dennett wrote:

> Bill Weston wrote:

> > Sorry, I'm being a bit daft here, but what is wrong with pubicly
> > deriving N from Z? Then sure you can initialize a Z with an N.

> What if Z supports operator -- (as it should)? N can't support
> that.

Why not?
Z N::operator-() const ;
Except that you don't even need to implement the function in the
derived class; the base class version works.

What N can't support is a non-const negate function. I would argue
that it shouldn't, either; that value types should only be modifiable
through assignment operators. (Of course, this creates a problem with
operator-=.)

> > But then I don't see much wrong with deriving Square from base
> > class Rectangle. So what? A square maybe has an extra data member
> > or two, and a N too, but the interface is right.

> Possibly, possibly not. What if the rectange include stretchX
> methods?

Then it is poorly designed?

The question is what the classes represent. If they are value
classes, representing abstract mathematical shapes, then I suspect
that they should be immutable (except through assignment), so there
should be no problem. If they are geometrical designs on a screen,
which can be modified, then you can presumably modify the value of
width and height separately, and a Square is not a Rectangle. (On the
other hand, has anyone actually wanted a class Square in this
context. At the most, I could imagine a function bool
Rectangle::isSquare.)

> > I know that mathmos construct Z as equivalence classes over pairs
> > of N's, and you *could* go down that constructive avenue in c++, I
> > guess, but just between friends a natural number is an integer,
> > right?

> Yes, but the mutating operations on N are a subset of those on Z.

Which I would interpret to mean that mutating operations on N are a
design error:-).

Seriously: N and Z are value objects. IMHO, derivation shouldn't
normally be used in conjunction with value objects -- that's not its
role.

--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

James Kanze

unread,
Mar 2, 2001, 4:54:26 PM3/2/01
to
Bill Weston wrote:

> Sorry, I'm being a bit daft here, but what is wrong with pubicly
> deriving N from Z?

First, of course, the question is backwards: you don't derive just
because you can, but because there is a concrete motivation for the
derivation. What is the motivation here?

The most frequent reason for derivation is subtyping. In this case,
derivation basically says that the derived class can be used
everywhere the base class can. I can negate a Z, so I must be able to
negate any class deriving from Z, for example. The formal condition
is that the derived class must support all operations legal on the
base class, and that, for every operation, the preconditions are no
stricter, and the postconditions at least as strict, as those for the
function in the base class.

If N and Z are immutable objects, with the standard operators
returning new instances, you can derive N from Z. The negation
operator will return a Z (even if the original type is an N). There
is still a problem with regards to assignment, but I think it is
manageable. Globally, however, I think it is probably cleaner to keep
the two classes separate (although they both might implement a common
interface, say Number).

The original poster wanted to do the reverse: derive Z from N. This
is definitely an error; a post condition for every operation on N is
that it return an N, and Z definitly won't meet this criterion.

> Then sure you can initialize a Z with an N. But then I don't
> see much wrong with deriving Square from base class Rectangle.

Same problem: it depends on the functionality. If the classes are
immutable, there is no problem.

> So what? A square maybe has an extra data member or two, and a N
> too, but the interface is right.

Actually, a square will typically have one less data member:-).

> I know that mathmos construct Z as equivalence classes over pairs of
> N's, and you *could* go down that constructive avenue in c++, I
> guess, but just between friends a natural number is an integer,
> right?

Yes, as long as it is immutable. If you allow mutation, there are
things you can do to an integer (like negate it) that you can't do to
a natural number.

> Come to think of it, using that representation, say a pair of
> unsigned longs, an N needs just as much room as a Z. What's the
> problem? Z operator-(const Z& lhs, const Z& rhs) still works when
> the arguments are N's -- it still only promises to return a Z.

Right. It doesn't prevent inheritance, although it might mean that
inheritance results in some type confusion in the minds of some
people.

--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

James Dennett

unread,
Mar 2, 2001, 6:23:36 PM3/2/01
to
James Kanze wrote:
>
> James Dennett wrote:
>
> > Bill Weston wrote:
>
> > > Sorry, I'm being a bit daft here, but what is wrong with pubicly
> > > deriving N from Z? Then sure you can initialize a Z with an N.
>
> > What if Z supports operator -- (as it should)? N can't support
> > that.
>
> Why not?

It's a mutating operation under which Z is closed (except of course
that our implementation of Z is likely to overflow eventually), and
under which N is not closed.

> Z N::operator-() const ;

I was talking of operator --, not operator -. The return value
could be in Z, but what of the object decremented?

The problem, as you explain further on, is the use of mutating
operations on "value" objects. Inheritance works much better
in the absence of mutators, where "better" means "intuitively."

> Except that you don't even need to implement the function in the
> derived class; the base class version works.
>
> What N can't support is a non-const negate function.

Which could be implemented in terms of operator --, if it could
support that. Proof that it can't support operator --.

> I would argue
> that it shouldn't, either; that value types should only be modifiable
> through assignment operators. (Of course, this creates a problem with
> operator-=.)
>
> > > But then I don't see much wrong with deriving Square from base
> > > class Rectangle. So what? A square maybe has an extra data member
> > > or two, and a N too, but the interface is right.
>
> > Possibly, possibly not. What if the rectange include stretchX
> > methods?
>
> Then it is poorly designed?

Quite possibly.

> The question is what the classes represent. If they are value
> classes, representing abstract mathematical shapes, then I suspect
> that they should be immutable (except through assignment), so there
> should be no problem. If they are geometrical designs on a screen,
> which can be modified, then you can presumably modify the value of
> width and height separately, and a Square is not a Rectangle. (On the
> other hand, has anyone actually wanted a class Square in this
> context. At the most, I could imagine a function bool
> Rectangle::isSquare.)

I agree wholeheartedly, and suspect that anyone who truly understands
inheritance (as used in C++) would also agree. (Anyone who doesn't,
please don't start a flame war!)

> > > I know that mathmos construct Z as equivalence classes over pairs
> > > of N's, and you *could* go down that constructive avenue in c++, I
> > > guess, but just between friends a natural number is an integer,
> > > right?
>
> > Yes, but the mutating operations on N are a subset of those on Z.
>
> Which I would interpret to mean that mutating operations on N are a
> design error:-).
>
> Seriously: N and Z are value objects. IMHO, derivation shouldn't
> normally be used in conjunction with value objects -- that's not its
> role.

Probably right. Although derivation could be used, it would be
unlikely to given an optimal design. I prefer to reserve inheritance
for cases where I have LSP substitutability and want run-time
polymorphism.

-- James Dennett

Nicola Musatti

unread,
Mar 5, 2001, 10:02:08 AM3/5/01
to

James Dennett wrote:
[...]


> I agree wholeheartedly, and suspect that anyone who truly understands
> inheritance (as used in C++) would also agree. (Anyone who doesn't,
> please don't start a flame war!)

You expect people to understand that they do not understand? I'm sure
even Socrates never went this far! :-)

Best regards,
Nicola Musatti

Bill Weston

unread,
Mar 10, 2001, 7:53:15 PM3/10/01
to
> > > Bill Weston wrote:
> >
> > > > Sorry, I'm being a bit daft here, but what is wrong with pubicly
> > > > deriving N from Z? Then sure you can initialize a Z with an N.
> >
> > > What if Z supports operator -- (as it should)? N can't support
> > > that.
> >
> > Why not?
>
> It's a mutating operation under which Z is closed (except of course
> that our implementation of Z is likely to overflow eventually), and
> under which N is not closed.
>

Thanks for reminding me about in place operators!
A silly thing to forget in c++ :-)
They do make N : Z hard to justify. But I love fighting a lost cause!...
What about using this modern template stuff to sidestep the 'Liskov
substitutability principle'?
Not sure it is the *right* thing to do in terms of design, but it is
interesting.
Not sure how this interacts with the virtual functionality, but FWIW...


template < typename Rep >
struct Z_ {
Rep rep;
virtual Z_& operator--(){--rep; return *this;}
};

template < typename Rep >
struct N_ {
Rep rep;
N_& operator--(){cannot_decrement_a_pointer;}
};

typedef Z_<int> Z;
typedef N_<int> N;

int main (int, char**)
{
Z z;
--z;
N n;
// If you uncomment the next line the compiler will complain (correctly).

//--n;
return 0;
};

Would be interested in people's reactions.
TIA,
Bill

Bill Weston

unread,
Mar 11, 2001, 1:34:26 PM3/11/01
to
[ hello, moderator, er, sorry I recently posted a reply to this with the
wrong example in it...]
// sorry, I just submitted a silly fragment of code, I was talking about
deriving N from Z and I omitted to do just that!
// this is more like what I meant, I hope.
// ie, Yes, derived; No, not a virtual function; Error message says can't
decrement a *natural number*, not a pointer.
// I am going mad. sorry.

template < typename Rep >
struct Z_ {
Rep rep;
Z_& operator--(){--rep; return *this;}
};

template < typename Rep >
struct N_ : Z_< Rep >{
Rep rep;
N_& operator--(){cannot_decrement_a_natural_number;}
};

typedef Z_< int > Z;
typedef N_< int > N;

int main (int, char**)
{
Z z;
--z;
N n;
// If you uncomment the next line the compiler will complain (correctly).

//--n;
return 0;
};


James Dennett wrote:

> > > Bill Weston wrote:
> >
> > > > Sorry, I'm being a bit daft here, but what is wrong with pubicly
> > > > deriving N from Z? Then sure you can initialize a Z with an N.
> >
> > > What if Z supports operator -- (as it should)? N can't support
> > > that.
> >
> > Why not?
>
> It's a mutating operation under which Z is closed (except of course
> that our implementation of Z is likely to overflow eventually), and
> under which N is not closed.
>

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bill Weston

unread,
Mar 11, 2001, 3:13:39 PM3/11/01
to
Michiel Salters wrote:

> Andrei Alexandrescu wrote:
>
> > "Michiel Salters" <sal...@lucent.com> wrote in message
> > news:3A961A1A...@lucent.com...
> > > > >From this we derive a class Z for integers:
>
> > > > class Z: public N {
> > > > public:
> > > > Z operator-(const Z&);
> > > > ...
> > > > }
>
> > > Thus Z is a proper superset of N.
>
> > Your whole post addresses some technical points that hide the main point: Z
> > is a superset of N, but this relationship cannot be expressed by
> > subclassing.
> > Andrei
>
> You're right. I should have stopped there.
>

I don't think we should stop there. I want to *really* understand this, if I
can. If c++ represents relationships between types primarily by public
derivation hierarchies, and numbers are pretty much THE prototypical hierarchy
(apologies to biologists who might think life itself more canonical :-) then
surely there should be a c++ expression of numbers. Otherwise its *really
dis-spiriting* to learn all these clever techniques and not be able to model
pretty much the first example that comes to mind.

It seems to me that the difficulty is when things qualify as special on account
of inhabiting special subspaces of their parameter spaces, rather than on
account of having extra dimensions to their parameters spaces. This is a pity
because its the specialness that is interesting, not the more detail.

So Square : Rectangle is problematic, but interesting
whereas Manager : Employee is fine. And boring.

So, to all you c++ gurus: J'ACCUSE !!
Also, I promise to spend my standard half-day's pay on the latest glossy tomb
that will tell me the standard lots of things I already learnt from the last
glossy tomb, AND explain how to model this sort of type hierarchy.
Otherwise I will try to start to accumulate
''InEffectiveUnExceptionalRetardedC--Idioms for Idiots'' .
BTW the latest glossy tomb I acquired, Andrei Alexandrescu's Modern C++ Design,
is packed full of fascinating stuff. But not (yet) how to model these
blackboard-font types.

Or is it really The Right Thing to have separate concrete types and a few
conversion operators? :-(
But that would beg the question... is it always right to have separate concrete
types and a few conversion ops?

My problem is one of frustration. It seems really hard to honestly say ''you
can't do <this> in C++''. It seems fairer to say ''you probably can do pretty
much <this> in C++, but it will cost you most of your free time for weeks, and a
big chunk of your sanity (and several trawls through above mentioned glossy
tombs)''.

Case in point is folk saying ''You can't derive N : Z because N then renegues on
Z's promise of an operator--(/*int maybe*/)''.

I read that too, in Meyers and the rest. And I did forget about in-place
operataors when I posted before. Then I remembered that you can work around
that for templated classes (reminded by Andrei's book) So if it were The Right
Thing to do to build a hierarchy then we can get around the Liskov thing using
templates, can't we? And if its not the right thing for Z,N,Q and friends then
mayybe it is for other types where more derived might be taken to mean more
special rather than more stuff? Lets face it, more stuff is for work, more
special is for Sunday afternoon (it is now Sunday afternoon in this timezone :-)

template < typename Rep >
struct Z_ {
Rep rep;
Z_& operator--(){--rep; return *this;}
};

template < typename Rep >
struct N_ : Z_< Rep >{
Rep rep;
N_& operator--(){cannot_decrement_a_natural_number;}

}; // its a real pity the error message has to syntactically scan
// think what fun you could have at work writing rude comments about the
project, the bosses,
// the quality of the coffee, all as 'effectively legal' lines of pay code, and
non of them to be
// looked at until someone calls the wrong thing ages hence (ie s/he is in the
wrong, not you)

typedef Z_< int > Z;
typedef N_< int > N;


>


> In case it's not clear to the OP: Using (public) inheritance states that
> that the derived class can be used where the base class can be used.
> This is known as the Liskov Substitution Principle (LSP)
>
> Supersets usually don't meet this requirement. For instance, the function

But but but sets and subsets most people have an intuitive grasp of.
And the recommended books *do* say something like ''think of public derivation
as is-a''.
But then it all goes horribly wrong. And we are supposed to grin and bear it?
;-)

> n!=n*(n-1)*(n-2)*...*2*1 in C++ has the interface N f(N n). The LSP
> states that if any Z is-a N it must be acceptable to f. Clearly this
> is not the case; f won't take -1.
>

I assumed OP must have meant N isa Z.

less frustrated after that rant, sorry,
Bill

Michael S. Terrazas

unread,
Mar 12, 2001, 2:59:01 PM3/12/01
to
"Bill Weston" <w.we...@ntlworld.com> wrote in message
news:3AABAA13...@ntlworld.com...
> Michiel Salters wrote:
<<snip>>

> > You're right. I should have stopped there.
>
> I don't think we should stop there. I want to *really* understand this,
if I
> can. If c++ represents relationships between types primarily by public
> derivation hierarchies, and numbers are pretty much THE prototypical
hierarchy
> (apologies to biologists who might think life itself more canonical :-)
then
> surely there should be a c++ expression of numbers. Otherwise its *really
> dis-spiriting* to learn all these clever techniques and not be able to
model
> pretty much the first example that comes to mind.

The problem is that mathematics does not speak in terms
of types when it says that the set of natural numbers is a
subset of the integers. The areas of math that are more
closely realted to types would be groups, rings, etc. And
when ring theory is being discussed, you talk of a set of
elements with two operations that have specific relations.
At that point there is no realtionship between (N, +, *) and
(Z, +, *) [actually, as I recall, (N, +, *) isn't even a ring since
every element of N does not have an inverse wrt +]

<<snip>>


> So Square : Rectangle is problematic, but interesting
> whereas Manager : Employee is fine. And boring.

Manager : Employee is also problematic when you try to
apply it to the real world. Much better is Employee as a
free-standing class with a role that the individual plays in
the organization. Actually it's a very informative and
useful example, especially in C++.

> So, to all you c++ gurus: J'ACCUSE !!

<<snip>>

The real problem is that most examples, such as this,
use a layman's interpretation of math and try to apply
C++ to it.

> But but but sets and subsets most people have an intuitive grasp of.
> And the recommended books *do* say something like ''think of public
derivation
> as is-a''.
> But then it all goes horribly wrong. And we are supposed to grin and bear
it?
> ;-)

The correct mathematical concepts for subclassing would
be subgroups, subrings and subfields. None of which hold
any intuition for the vast majority of people. Set theory
doesn't talk of operations, other than element of, union,
intersection, set subtraction and the like. None of these
operations apply to the members of the set. All member
operations are external to the set and are applied using an
algebra or a calculus.

So, the upshot is, subclasses have more to do with the
nature of the operations upon objects than on an initial
appearance of a subset relationship amongst the
sets to which the operations apply.

--
Mike Terrazas
MLT & Associates

Brian McNamara!

unread,
Mar 12, 2001, 3:01:20 PM3/12/01
to
Bill Weston <w.we...@ntlworld.com> once said:
>Michiel Salters wrote:
>> Andrei Alexandrescu wrote:
>> > "Michiel Salters" <sal...@lucent.com> wrote in message
>> > > > >From this we derive a class Z for integers:

>> > > > class Z: public N {
>> > > > public:
>> > > > Z operator-(const Z&);
>> > > > ...
>> > > > }

>> > > Thus Z is a proper superset of N.

>> > Your whole post addresses some technical points that hide the main
>> > point: Z is a superset of N, but this relationship cannot be
>> > expressed by subclassing.

>> You're right. I should have stopped there.

>I don't think we should stop there. I want to *really* understand this,

Me too. I think maybe I do, but I've never really forced myself to
think about it this much until your colorful post. :)

>It seems to me that the difficulty is when things qualify as special on
>account of inhabiting special subspaces of their parameter spaces, rather
>than on account of having extra dimensions to their parameters spaces.
>This is a pity because its the specialness that is interesting, not the
>more detail.
>
>So Square : Rectangle is problematic, but interesting
>whereas Manager : Employee is fine. And boring.

Yep.

>So, to all you c++ gurus: J'ACCUSE !!

Ok, I'm stepping up to the plate.

First, a brief review. "Z is derived from N" is bad because then every
Z isa N, which means we can have Ns with the value "-2". "N is derived
from Z" is bad because then N must support operator--, which it cannot,
as the value-space of N isn't closed on that operation.

My suspicion: The only problem with the latter is in-place mutators
(actually covariant arguments, as LSP tells us, but let's keep it simple).

Implication: "N is derived from 'const Z'" would be just fine, if we
could say such a thing.

Proposed solution: (I'm using "int" as the Rep for simplicity)

struct const_Z {
int rep;
const_Z( int x ) : rep(x) {}
};

struct Z : public const_Z {
Z( int x ) : const_Z(x) {}
Z& operator--() { --rep; return *this; }
};

struct N : public const_Z {
N( int x ) : const_Z(x) { assert(x>=0); }
// operator Z() ?
};

This still suffers from the fact that const_Z and "const Z" are not the
same thing, alas. Perhaps we can join the library designers who must
sort out iterator<T>, const_iterator<T>, and iterator<const T> for a
good cry. :)

We could give up on side-effects altogether, and go program in Haskell,
which I am occasionally tempted to do, but that seems like just running
away from the problem.

In any case, perhaps there is something to the notion that "const X"
should be a legal type to derive from. (The people who were arguing for
const-constructors would probably have something to say here, too.) I
also just noticed during this thread that I typically think implicit
conversions are bad, except when they satisfy LSP, in which case
they're... less bad. :)


>template < typename Rep >
>struct N_ : Z_< Rep >{
> Rep rep;
> N_& operator--(){cannot_decrement_a_natural_number;}
>}; // its a real pity the error message has to syntactically scan

More than that. An implementation can flag this as an error even if
the template is never instantiated, as no valid specialization exists
(see 14.6/7 for details).

--
Brian M. McNamara lor...@acm.org : I am a parsing fool!
** Reduce - Reuse - Recycle ** : (Where's my medication? ;) )

Andrei Alexandrescu

unread,
Mar 12, 2001, 3:50:02 PM3/12/01
to
"Bill Weston" <w.we...@ntlworld.com> wrote in message
news:3AABAA13...@ntlworld.com...
> I don't think we should stop there. I want to *really* understand this,
if I
> can. If c++ represents relationships between types primarily by public
> derivation hierarchies, and numbers are pretty much THE prototypical
hierarchy
> (apologies to biologists who might think life itself more canonical :-)
then
> surely there should be a c++ expression of numbers. Otherwise its *really
> dis-spiriting* to learn all these clever techniques and not be able to
model
> pretty much the first example that comes to mind.

But hold on, didn't anybody mention inheritance is a pretty limited tool
that pretty much sucks? :o)

You know, this thing with the relationship between N and Z is interesting.
For example, as others mentioned, think of operator-(const N&, const N&).
This has results in N if you work in N only (and has undefined results for
certain operands), and in Z if you choose to operate with Z. So basically
you must not only define N and Z and their operations, but you have to
specify what set you will choose for certain operations.

The polymorphic model of the world goes from general to specialized. For
example, something that many OO enthusiasts don't understand is that in
statically-type OO languages you shouldn't add public member functions when
you derive. In this respect, N should inherit Z. But then, you're pretty
much away from any benefits that type safety might bring you.

> BTW the latest glossy tomb I acquired, Andrei Alexandrescu's Modern C++
Design,
> is packed full of fascinating stuff. But not (yet) how to model these
> blackboard-font types.

:">

> Or is it really The Right Thing to have separate concrete types and a few
> conversion operators? :-(

I think this is the case with the languages we have today...

> But that would beg the question... is it always right to have separate
concrete
> types and a few conversion ops?

I don't know... the way I see it, is that most type-related stuff is way
beyond anything inheritance can offer. Then, there are not many other tools
that express relationships between types, so basically people tend to think
of inheritance whenever a type-related problem comes across. If all you have
a hammer etc.

> template < typename Rep >
> struct Z_ {
> Rep rep;
> Z_& operator--(){--rep; return *this;}
> };
>
> template < typename Rep >
> struct N_ : Z_< Rep >{
> Rep rep;
> N_& operator--(){cannot_decrement_a_natural_number;}
> }; // its a real pity the error message has to syntactically scan
> // think what fun you could have at work writing rude comments about the
> project, the bosses,
> // the quality of the coffee, all as 'effectively legal' lines of pay
code, and
> non of them to be
> // looked at until someone calls the wrong thing ages hence (ie s/he is in
the
> wrong, not you)
>
> typedef Z_< int > Z;
> typedef N_< int > N;

Your code, although I'm not sure I understand it in its entirety,
illustrates an approach that I find very effective: you build a template
suprastructure that contains the rather baroque set of operations, on top of
a small infrastructure (the template arguments), infrastructure that defines
the basic structural and functional capabilities of the whole built
structure. I'm not claim that that belongs to me, mind you :o).

I have this annoying habit of trying to create excitement about my upcoming
articles on CUJ Experts Online. This is because I put a lot of energy in
writing those articles and, heck, almost nobody reads them. So here goes. My
next article there, should I get to write it, illustrates how you can build
pretty much any implementation of std::basic_string you might think of
(reference-counted or not, with smart or dumb iterators, with the small
string optimization or not, and so on) by building a shell of an surprising
small (80-120 lines of code) structural and functional core. It's like you
do the job of multiple programs who implement a well-tuned version of
std::basic_string, but instead of doing the jobs SEQUENTIALLY, you do them
in PARALLEL and benefit of these finely-crafted implementations
SIMULTANEOUSLY. For example, you can combine the small string optimization
with a reference-counted implementation very easy. The article will contain
actual code of such mixable-matcheable std::basic_string implementations,
and will show how you are 100 LINES OF CODE away from pretty much any
std::basic_string implementation or optimization you fancy.

Come think of it: it's hard enough to build ONE good, bug-free
implementation of std::basic_string, but the policy-based technology will
allow me to come with FIVE or more (depends on the time I'll have)
standard-compliant, finely-built implementations in ONE SHOT. And I'm not
talking about toy code here - I'm taking about industry-strength
std::basic_string implementations with real optimizations and that you can
use in real apps. Of course I can't claim my implementations are bug-free,
but at any rate: if a bug is found in the shell, you correct all
implementations at once; if a bug is found in the core, it's easier to find
and correct because you have to track only a small amount of code.

> I assumed OP must have meant N isa Z.

N isa restricted Z... Z isa generalized N... neither of these relationships
can be expressed comfortably by inheritance. In my humble opinion, to claim
the contrary tantamounts to admiring emeperor's clothes.


Andrei

James Kanze

unread,
Mar 12, 2001, 4:20:02 PM3/12/01
to
Bill Weston wrote:

> Michiel Salters wrote:

> > Andrei Alexandrescu wrote:

> > > "Michiel Salters" <sal...@lucent.com> wrote in message
> > > news:3A961A1A...@lucent.com...
> > > > > >From this we derive a class Z for integers:

> > > > > class Z: public N {
> > > > > public:
> > > > > Z operator-(const Z&);
> > > > > ...
> > > > > }

> > > > Thus Z is a proper superset of N.

> > > Your whole post addresses some technical points that hide the
> > > main point: Z is a superset of N, but this relationship cannot
> > > be expressed by subclassing. Andrei

> > You're right. I should have stopped there.

> I don't think we should stop there. I want to *really* understand
> this, if I can. If c++ represents relationships between types
> primarily by public derivation hierarchies, and numbers are pretty
> much THE prototypical hierarchy (apologies to biologists who might
> think life itself more canonical :-) then surely there should be a
> c++ expression of numbers. Otherwise its *really dis-spiriting* to
> learn all these clever techniques and not be able to model pretty
> much the first example that comes to mind.

Your *if* is false, so the rest doesn't hold either. C++ represents
relationships between types in many different ways, depending on the
relationship. Public derivation is most frequently used for what is
often called an isA relationship; in this case, Z isA N is manifestly
false, so you wouldn't want to use inheritance. More generally,
however, just isA isn't really exact; it's a particular type of isA,
which guarantees that an instance of the derived class can be used
where ever an instance of the base class can.

There are, of course, other uses of public inheritance; name injection
comes to mind. But they aren't relevant here either.

--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

Dietmar Kuehl

unread,
Mar 13, 2001, 6:07:37 PM3/13/01
to
Hi,
Bill Weston (w.we...@ntlworld.com) wrote:
: Michiel Salters wrote:

: > Andrei Alexandrescu wrote:
: >
: > > "Michiel Salters" <sal...@lucent.com> wrote in message
: > > news:3A961A1A...@lucent.com...
: > > > > >From this we derive a class Z for integers:
: >
: > > > > class Z: public N {
: > > > > public:
: > > > > Z operator-(const Z&);
: > > > > ...
: > > > > }
: >
: > > > Thus Z is a proper superset of N.
: >
: > > Your whole post addresses some technical points that hide the main
point: Z
: > > is a superset of N, but this relationship cannot be expressed by
: > > subclassing.
: > > Andrei
: >
: > You're right. I should have stopped there.
: >

: I don't think we should stop there. I want to *really* understand this, if
I
: can.

That's pretty simple: Object oriented inheritance models subclassing,
meaning that the derived class models a subset of the base class. That
is, why it is called IS-A relationship: Each object of the derived
class is-an object of the base class. Now, in your example, an integer
IS-NOT-A natural number. Since this inheritance would violate the
semantics, you should not do it this way.

Now you might be tempted to do it the other way around: A natural number
IS-AN integer, isn't it? For example:

class Z {
/* integer numbers, probably derived from Q, the
* rational numbers... */
};

class N: public Z {
/* natural numbers, derived from the integers */
};

This is a Bad Idea, too! Why's that? Because the natural numbers do not
provide all operations applicable to integers. In particular, you cannot
substract an arbitrary natural, eg. 5, from another arbitrary natural
number, eg. 2, because the result is not a natural number:

N n5(5), n2(2);
n2 -= n5; //oops: the result would be a negative number!

Since you can savely substract arbitrary integers and stay in the realm
of integers, a natural number does not behave like an integer and is
thus violating the Liskov Substitution Principle which basically says
that a derived class at least shall support all operations of the base
class.

[Well, if you make the number immutable and in particular, if you do
not support a -= operation in the base class, it might be reasonable to
actually derive the class representing natural numbers from the class
representing integers. However, you have to be pretty careful and the
use of the resulting class may not be natural (pun intended).]

Actually, why would anybody want to derive classes like this in the
first place? Apparently, you want to share algorithms between the
different number representation. However, this is best modeled by a
"supports feature" relationship as is used in Generic Programming: All
algorithms would be templated on the number type with certain
requirements imposed on the number representation. Not everything is
best dealt with by inheritance...
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

Dietmar Kuehl

unread,
Mar 13, 2001, 6:08:00 PM3/13/01
to
Hi,
Andrei Alexandrescu (andre...@hotmail.com) wrote:
: The polymorphic model of the world goes from general to specialized. For

: example, something that many OO enthusiasts don't understand is that in
: statically-type OO languages you shouldn't add public member functions when
: you derive.

Huh? The more specific type may very well expose more specific features
and, correspondingly, there may be additional operations in the derived
class. Hence, a derived class might have additional public member
functions! These cannot be used in a dynamically polymorph setting
operating on the base class but can be used when manipulating objects
of the derived class, eg. during creation time (ie. construction and
possible post-construction set up). For example, a popular member added
when deriving class is a constructor: An abstract base class, in some
sense, does not have any constructor because it cannot be constructed.
A concrete class does have an additional member, namely at least some
constructor (yes, technically, there is a constructor in the abstract
base class, too, but this can and often is actually protected).


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Branimir Maksimovic

unread,
Mar 13, 2001, 6:12:07 PM3/13/01
to

"Bill Weston" <w.we...@ntlworld.com> wrote in message
news:3AABAA13...@ntlworld.com...

I think that you have chosen wrong example. Not much of an relationships
between algebaric structures and sets of numbers except
functions(all functions perform conversions from one set into other in math)
and operators that are part of those functions.
Perhaps modeling relationship between water, ice, liquid, gas
and aggregate state is more interesting :)

>
> It seems to me that the difficulty is when things qualify as special on
account
> of inhabiting special subspaces of their parameter spaces, rather than on
> account of having extra dimensions to their parameters spaces. This is a
pity
> because its the specialness that is interesting, not the more detail.
>
> So Square : Rectangle is problematic, but interesting
> whereas Manager : Employee is fine. And boring.
>
> So, to all you c++ gurus: J'ACCUSE !!
> Also, I promise to spend my standard half-day's pay on the latest glossy
tomb
> that will tell me the standard lots of things I already learnt from the
last
> glossy tomb, AND explain how to model this sort of type hierarchy.
> Otherwise I will try to start to accumulate
> ''InEffectiveUnExceptionalRetardedC--Idioms for Idiots'' .
> BTW the latest glossy tomb I acquired, Andrei Alexandrescu's Modern C++
Design,
> is packed full of fascinating stuff. But not (yet) how to model these
> blackboard-font types.
>
> Or is it really The Right Thing to have separate concrete types and a few
> conversion operators? :-(

Conversion operators are much more powerfull then inheritance
in this case, if you think about it. Numbers are just numbers, there are no
special member functions to associate with them.

> But that would beg the question... is it always right to have separate
concrete
> types and a few conversion ops?

Depends on situation. There is no such thing as ultimate solution for
all problems.

Depends by what one understand by is-a.
N is not Z. Z is not N.
Natural number is-a element of N.
Natural number is-a element of Z.
Integer is-a element of Z, but not of N.
N is-a set of naturals and Z is-a set
of integers including zero.
Question : Is natural number an integer?
No. Natural number is-a positive integer.
So we have positive integers (naturals),
negative integers and zero in Z.
So N is-a subset of Z.
Is subset an element of set ? :)
Not sure.
But then again Z is-a subset too, so as Q,
so as R :) So we are talking here more about collections
then about type classifications.
Perhaps if you find abstract interface for:

template <class Type>
class set {/*.....*/};

template <class Type>
class element{/* ....... */ };

template <class SetType>
class subset:public <SetType> {/*.....*/};

class Z: public element<set<Z> >{/*.....*/};

class PositiveZ: public Z{ /*.........*/ }; // here you can freely define --
operation
// that can yield Z :)

class N: public element<set<N> >, public PositiveZ{/*.....*/};

template <>
class set<N>: public subset<set<Z> >{/*........*/};


perhaps this can be worked out. , but ...... somehow
this is more like container problem then inheritance one :)

>
> > n!=n*(n-1)*(n-2)*...*2*1 in C++ has the interface N f(N n). The LSP
> > states that if any Z is-a N it must be acceptable to f. Clearly this
> > is not the case; f won't take -1.
> >
>
> I assumed OP must have meant N isa Z.
>
> less frustrated after that rant, sorry,
> Bill

Greetings, Bane.

Andrei Alexandrescu

unread,
Mar 13, 2001, 11:17:12 PM3/13/01
to
"Dietmar Kuehl" <ku...@ramsen.informatik.uni-konstanz.de> wrote in message
news:98jk8v$4t$3...@news.BelWue.DE...

> Andrei Alexandrescu (andre...@hotmail.com) wrote:
> : The polymorphic model of the world goes from general to specialized. For
> : example, something that many OO enthusiasts don't understand is that in
> : statically-type OO languages you shouldn't add public member functions
when
> : you derive.
>
> Huh? The more specific type may very well expose more specific features
> and, correspondingly, there may be additional operations in the derived
> class. Hence, a derived class might have additional public member
> functions! These cannot be used in a dynamically polymorph setting
> operating on the base class but can be used when manipulating objects
> of the derived class, eg. during creation time (ie. construction and
> possible post-construction set up).

That's what I was referring to: inheritance is built towards supporting
dynamic dispatch, and adding public member functions works against that.
They will continuously invite programmers with siren songs and gracious,
temptating moves, to cast.

> For example, a popular member added
> when deriving class is a constructor: An abstract base class, in some
> sense, does not have any constructor because it cannot be constructed.

I concede that :o).


Andrei

Mary K. Kuhner

unread,
Mar 14, 2001, 10:35:55 AM3/14/01
to
I'm really going to stick my neck out here, because I am a
relatively novice C++ programmer. But the conclusion I've
come to by poring over _Effective C++_ and the FAQ, and
working on my own medium-sized project, is that inheritance
as I've found it useful is not really a relationship
between parent and child class at all. This is especially
apparent if the base class is abstract (which a lot of authorities
encourage).

What it is, is a relationship among *siblings*. If I have
several classes inheriting publically from a common base,
that's because I want to be able to write code that treats
all the siblings as interchangable. If I don't intend to
have a set of siblings, or I don't want to treat them
interchangably, I would be better off not inheriting.

The base class is not a thing. It's a compendium of
the ways in which the siblings are meant to be interchangable,
and an alias for an arbitrarily-chosen sibling.

Neither integers nor natural numbers are suitable as a rundown
of the ways in which integers and natural numbers ought to
be interchangable, so neither one is a suitable base class
for the other.

It seems to me that looking at it this way--emphasizing the
sibling relationship, not the parent-child one--immediately
rules out a lot of the dysfunctional inheritance patterns.
Squares and rectangles are not going to be interchangable
if we give the user independent control of their
dimensions, so either they don't inherit from the same base,
or that base doesn't offer any such functions.

In this view, a concrete base class is just an optimization
in which we acknowledge that one of the siblings has no
personality of its own beyond the interchangable parts, so
we don't need to bother defining it separately. You can
always work out such cases by considering them as siblings.
Concrete inheritance is then proper only if one sibling is
completely devoid of anything that wouldn't be an
appropriate specification of the interchangability contract
for the sibship.

Alas, this does point out that interchangability is in the
eye of the user code (just as Ostrich inheriting from Bird
may be fine if we only need to model calorie intake, not
flying). But one practically always needs to imagine
something about the user code before defining a class
heirarchy anyway.

The sibling-group concept works better for me than thinking about
whether Derived ISA Base. I think the natural-number example
is a good illustration of why.

Mary Kuhner mkku...@genetics.washington.edu

Eugene Karpachov

unread,
Mar 14, 2001, 10:37:12 AM3/14/01
to
13 Mar 2001 18:12:07 -0500 Branimir Maksimovic написал:

>Question : Is natural number an integer?
>No. Natural number is-a positive integer.

You are making mess.

Is positive integer an integer? Of course yes.

--
jk

Dylan Nicholson

unread,
Mar 14, 2001, 1:57:40 PM3/14/01
to
Andrei Alexandrescu <andre...@hotmail.com> wrote in message
news:98m9ql$2hrvt$1...@ID-14036.news.dfncis.de...

> "Dietmar Kuehl" <ku...@ramsen.informatik.uni-konstanz.de> wrote in message
> news:98jk8v$4t$3...@news.BelWue.DE...
> > Andrei Alexandrescu (andre...@hotmail.com) wrote:
> > : The polymorphic model of the world goes from general to specialized.
For
> > : example, something that many OO enthusiasts don't understand is that
in
> > : statically-type OO languages you shouldn't add public member functions
> when
> > : you derive.
> >
> > Huh? The more specific type may very well expose more specific features
> > and, correspondingly, there may be additional operations in the derived
> > class. Hence, a derived class might have additional public member
> > functions! These cannot be used in a dynamically polymorph setting
> > operating on the base class but can be used when manipulating objects
> > of the derived class, eg. during creation time (ie. construction and
> > possible post-construction set up).
>
> That's what I was referring to: inheritance is built towards supporting
> dynamic dispatch, and adding public member functions works against that.
> They will continuously invite programmers with siren songs and gracious,
> temptating moves, to cast.
>
"is built towards" != "only appropriate use". You may disagree, but it is
perfectly valid to use inheritance to create a new static types with added
functionality. While it is arguably "better form" to use _private_
inheritance and inject the names from the base class manually, I just don't
see that the supposed benefit is worth it. The reality is that we often
have to work with pre-existing classes with extensive interfaces - if we
want to provide a new class with all that functionality, plus extra, and you
wish to maintain consistency of interface (i.e. member function syntax),
then the only reasonable option C++ offers is public inheritance. I, and
I'm sure many many others, do it on a day to day basis, without shame, and
without it ever causing a problem in practice. To be honest, I can't think
of a reason why it ever should. If the language designers really meant for
public inheritance to have such a restricted use, they could have easily
enforced rules to that effect. Your belief is that the only valid use for
public inheritance is to override virtual funtions (i.e. dynamic
polymorphism) - some of us find it useful for a myriad of purposes. Part of
the popularity, and arguably a lot of the strength of C++, comes from the
fact that it can be used, safely and reasonably, in many different ways, for
many different programming styles and paradigms. It is also, as is well
evidenced by the discussions on this newsgroup, a healthy source of much
heated debate!

Dylan

James Kanze

unread,
Mar 14, 2001, 2:00:28 PM3/14/01
to
Dietmar Kuehl wrote:

> Andrei Alexandrescu (andre...@hotmail.com) wrote:
> > The polymorphic model of the world goes from general to
> > specialized. For example, something that many OO enthusiasts don't
> > understand is that in statically-type OO languages you shouldn't
> > add public member functions when you derive.

> Huh? The more specific type may very well expose more specific
> features and, correspondingly, there may be additional operations in
> the derived class.

One very typical case: a class implements several contracts, each of
which is specified by an abstract base class. An obvious example, my
"model" class for a table might typically implement TableModel,
TableEventHandler and DataChangedListener. Of course, the table only
sees the TableModel interface, the table event source the
TableEventHandler, and the data source a DataChangedListener, but for
each interface, the actual class implements additional public
functions.

There is some argument for not providing public functions which aren't
declared in some base class; I tend to consider it somewhat along the
lines of not deriving from a concrete class -- generally a good idea,
but with enough exceptions that I don't make it a hard rule. It
wouldn't be too hard to imagine a case where my table model is the
only class interested in changes in data from a particular source, and
so rather than define an arbitrary interface, the data source "knows"
the table model interface directly, and uses one or more public
functions declared in that interface.

--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

James Kanze

unread,
Mar 14, 2001, 2:01:17 PM3/14/01
to
Andrei Alexandrescu wrote:

> "Dietmar Kuehl" <ku...@ramsen.informatik.uni-konstanz.de> wrote in message
> news:98jk8v$4t$3...@news.BelWue.DE...
> > Andrei Alexandrescu (andre...@hotmail.com) wrote:
> > > The polymorphic model of the world goes from general to
> > > specialized. For example, something that many OO enthusiasts
> > > don't understand is that in statically-type OO languages you
> > > shouldn't add public member functions when you derive.

> > Huh? The more specific type may very well expose more specific
> > features and, correspondingly, there may be additional operations
> > in the derived class. Hence, a derived class might have additional
> > public member functions! These cannot be used in a dynamically
> > polymorph setting operating on the base class but can be used when
> > manipulating objects of the derived class, eg. during creation
> > time (ie. construction and possible post-construction set up).

> That's what I was referring to: inheritance is built towards
> supporting dynamic dispatch, and adding public member functions
> works against that. They will continuously invite programmers with
> siren songs and gracious, temptating moves, to cast.

But this has nothing to do with adding public member functions. It
can occur anytime a class implements an "extended" contract. And it
isn't always wrong: in a GUI hierarchy, only some of the component
classes will implement InputEventHandler, for example. And the code
which generates imput events will probably use some sort of RTTI on
the component which currently has the focus, in order to know whether
to pass it the event immediately, or to pass the event to some more
global handler. The question: "do you implement the input event
handler contract?" seems a legitimate one to ask. Depending on the
context, using RTTI to ask it might be right.

--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

Gerhard Menzl

unread,
Mar 16, 2001, 12:58:04 PM3/16/01
to
Dylan Nicholson wrote:

> "is built towards" != "only appropriate use". You may disagree, but
> it is perfectly valid to use inheritance to create a new static types
> with added functionality. While it is arguably "better form" to use
> _private_ inheritance and inject the names from the base class
> manually, I just don't see that the supposed benefit is worth it. The
> reality is that we often have to work with pre-existing classes with
> extensive interfaces - if we want to provide a new class with all that
> functionality, plus extra, and you wish to maintain consistency of
> interface (i.e. member function syntax), then the only reasonable
> option C++ offers is public inheritance.

What is unreasonable about containment? The extra typing involved?

> I, and I'm sure many many others, do it on a day to day basis, without
> shame, and without it ever causing a problem in practice. To be
> honest, I can't think of a reason why it ever should.

I can think of several:

- it leads to huge, unmaintainable class hierachies,
- it causes bloated interfaces that need to be looked up in a dozen
different places,
- it incurs a maximum amount of coupling,
- it increases the danger of slicing

Gerhard Menzl

Francis Glassborow

unread,
Mar 16, 2001, 2:54:17 PM3/16/01
to
In article <3AAF9B60...@divalsim.it>, Nicola Musatti
<obje...@divalsim.it> writes
>I still think that a concrete base class, i.e. an instantiable one, is
>very often a mistake. It is true on the other hand that the
>identification of commonalities among classes is often connected with
>the existence of operations which are not only conceptually common, but
>which are performed in exactly the same way and for which it may be
>worthwhile to factor together the implementation in the base class, even
>though there are practical considerations which might lead to the
>opposite conclusion.

It strikes me that this factoring of commonality into a concrete base
class might be assisted if we had a way to combine private inheritance
(thereby suppressing the implicit derived to base conversion) with a
class scope using directive (there is no such thing at the moment) to
republish the base class interfaces en masse.

--
Francis Glassborow
See http://www.accu.org for details of The ACCU Spring Conference, 2001
(includes many regular participants to C & C++ newsgroups)

James Kanze

unread,
Mar 16, 2001, 6:04:51 PM3/16/01
to
Nicola Musatti wrote:

> "Mary K. Kuhner" wrote:
> [...]


> > What it is, is a relationship among *siblings*. If I have several
> > classes inheriting publically from a common base, that's because I
> > want to be able to write code that treats all the siblings as
> > interchangable. If I don't intend to have a set of siblings, or I
> > don't want to treat them interchangably, I would be better off not
> > inheriting.

> Well said! In other words inheritance is a way to explicitly express
> the commonality among a given set of classes in order to be able to
> use them interchangeably at runtime. This is in contrast, for
> instance, with templates which are a way to exploit the implicit
> commonality among a set of classes in order to use them
> interchangeably at compile time.

I don't really like the term "commonality". It can mean too many
things. Inheritance can be used for different purposes, but the
different purposes are distinct, and each deserves a more exact term
than simply commonality.

Most of the time, for example, my base classes (abstract) define a
contract (or an interface, if you prefer); what is common between the
derived classes is that they implement that contract. It's not just
any random commonality.

Note that it often makes sense to have an abstract base class even if
there is only one class which derives from it (for now, anyway). I
find it logical to radically separate the contract (which cannot
easily be changed without rework in all of the clients) from the
implementation (which can be modified as often as you like). If
nothing else, the separation effectively documents what is guaranteed,
and what is only a accident of the current implementation.

> > The base class is not a thing. It's a compendium of the ways in
> > which the siblings are meant to be interchangable, and an alias
> > for an arbitrarily-chosen sibling.

> [...]


> > It seems to me that looking at it this way--emphasizing the
> > sibling relationship, not the parent-child one--immediately rules
> > out a lot of the dysfunctional inheritance patterns.

> [...]


> > In this view, a concrete base class is just an optimization in
> > which we acknowledge that one of the siblings has no personality
> > of its own beyond the interchangable parts, so we don't need to
> > bother defining it separately. You can always work out such cases
> > by considering them as siblings. Concrete inheritance is then
> > proper only if one sibling is completely devoid of anything that
> > wouldn't be an appropriate specification of the interchangability
> > contract for the sibship.

> I still think that a concrete base class, i.e. an instantiable one,


> is very often a mistake. It is true on the other hand that the

> identification of commonalities among classes is often connected
> with the existence of operations which are not only conceptually


> common, but which are performed in exactly the same way and for
> which it may be worthwhile to factor together the implementation in
> the base class, even though there are practical considerations which
> might lead to the opposite conclusion.

The best way to factor out common implementation details is USUALLY
delegation, not inheritance. There are, however, exceptions; in such
cases, private inheritance is generally to be preferred; it is likely
in such cases that the functions which implement the commonality will
also be protected. (A frequent reason for using inheritance in this
case is that parts of the common functionality need to be customized
according to the actual class.)

> > Alas, this does point out that interchangability is in the eye of
> > the user code (just as Ostrich inheriting from Bird may be fine if
> > we only need to model calorie intake, not flying). But one
> > practically always needs to imagine something about the user code
> > before defining a class heirarchy anyway.

> Which is the whole point about software design and programming! I
> would contend that most classification schemes tend to serve a
> purpose rather than being absolute.

> > The sibling-group concept works better for me than thinking about
> > whether Derived ISA Base. I think the natural-number example is a
> > good illustration of why.

I think that any simple relationship based on vague commonality is
bound to fail in the long run. I would insist that the exact
relationship be specified: A implements the contract defined in B, or
A is a customization of B, or ...

--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

Victor Bazarov

unread,
Mar 16, 2001, 9:39:57 PM3/16/01
to
"Harri" <n...@email.sorry> wrote...

> > >No. Natural number is-a positive integer.
> >
> > You are making mess.
> >
> > Is positive integer an integer? Of course yes.
>
> No, positive integer is not an integer. You can't do everything to a
> positive integer that you can to an integer. (I.E. assigning a
negative
> integer to it).

I think you're twisting the definition. The LSP says that
"IS-A" relationship means that you can use the derived class
anywhere the base class can be _used_. Can you use a positive
integer where an integer is expected? Yes, you can. Assignment
is _not_ inherited and therefore you cannot put the behaviour of
the class while something is assigned to it as a requirement for
the "IS-A" relationship. After all I _can_ assign a negative
integer to a positive integer. It doesn't mean that it will be
the same (or that it will hold the negative value). It may lose
the sign, it may throw an exception...

Victor
--
Please remove capital A's from my address when replying by mail

Dylan Nicholson

unread,
Mar 16, 2001, 9:56:04 PM3/16/01
to
Dennis Yelle <denn...@jps.net> wrote in message
news:3AAFDDA0...@jps.net...
> Dylan Nicholson wrote:
> [...]

> > Your belief is that the only valid use for
> > public inheritance is to override virtual funtions (i.e. dynamic
> > polymorphism) - some of us find it useful for a myriad of purposes.
Part of
> > the popularity, and arguably a lot of the strength of C++, comes from
the
> > fact that it can be used, safely and reasonably, in many different ways,
for
> > many different programming styles and paradigms. It is also, as is well
> > evidenced by the discussions on this newsgroup, a healthy source of much
> > heated debate!
>
> Good. You are willing to take the other side of this argument.
> Thanks.
>
> Please give us a list of this "myriad of purposes" you
> refer to above. I really want this list, I am not just out
> to beat you over the head with it. My opinion is that most of
> them will actually indicate flaws in the C++ language itself,
> so, please provide a list and maybe it will lead to improvements
> in the next version of C++.
>

1) Inherit of type information. Widely used in STL (eg binary_function,
unary_function, iterator)
2) As a form of template typedef-ing (seen in recent gotw thread) - I accept
this IS a language flaw.
3) Extending an existing concrete class, often one provided by a 3rd party
library. I often find library classes provide 99% of the functionality I
need, quite often have exactly the interface I need, but not quite all of
the functionality. A library class that is designed to be extensible can go
one of two ways: provide virtual functions you can override directly, or
secondly provide some sort of callback mechanism - including the possibility
of an abstract interface class it can use to configure itself, whereby the
user can pass an instance of their own version of this class to the object.
While I can see advantages in the latter approach, it generally isn't as
efficient, and seems to be rarely used.
4) As a "Quick and easy" method of making use of the functionality and
interface of another class. I did this recently whereby I had to provide an
iterator class that worked exactly the same as the std::map iterator class,
except for operator* that returned only the key and not the value.

These last two methods would probably be cases where I would be told to use
private inheritance and manual inject the names from the base class, or
containment and manually forward every method to the contained class. But
why? The only reason for doing either of these is to prevent people
treating my class as an object of the base class, and I agree, they
shouldn't be allowed to. But I am not going to make an maintenance
nightmare for myself in order to achieve this - I'm happy enough just to
document the intended usage/limitations along with the class. Furthermore, I
have never seen anyone run into a problem whereby they attempted to use a
derived class polymorpically when it wasn't intended as such.

Perhaps you're right, it is a flaw in the language and what we need is
"public static" inheritance, whereby the all members are inherited
publically, but you cannot automatically cast to the base class. Can anyone
think of any real problems with this?

Alf P. Steinbach

unread,
Mar 15, 2001, 4:39:06 AM3/15/01
to
"Bill Weston" <w.we...@ntlworld.com> wrote in message news:3AABAA13...@ntlworld.com...
> Michiel Salters wrote:
>
> > Andrei Alexandrescu wrote:
> >
> > > "Michiel Salters" <sal...@lucent.com> wrote in message
> > > news:3A961A1A...@lucent.com...
> > > > > >From this we derive a class Z for integers:
> >
> > > > > class Z: public N {
> > > > > public:
> > > > > Z operator-(const Z&);
> > > > > ...
> > > > > }
> >
> > > > Thus Z is a proper superset of N.
> >
> > > Your whole post addresses some technical points that hide the main point: Z
> > > is a superset of N, but this relationship cannot be expressed by
> > > subclassing.
> > > Andrei
> >
> > You're right. I should have stopped there.
> >
>
> I don't think we should stop there. I want to *really* understand this, if I
> can.


This is an old discussion, where some answers are known. First, let's
distinguish between the *value set* Values(t) of a type t, which is the
set of possible values of type t, and the *operations set* Operations(t).

Then let's consider A and B, where B is a subclass of A in the object-
oriented sense, obeying the Liskov substitution principle where an
objectB should always be able to play the role of an objectA. In this
case

Values(A) >= Values(B)
Operations(A) <= Operations(B)

(for Values because the A set includes the B set).

Then consider the real numbers R versus the integers I:

Values(R) >= Values(I),
Operations(R) ?? Operations(I)

I'd say, off the top of my head, that "??" is in reality ">=" (superset
or equal), which goes the opposite way of OO Liskov substitution. That
is, except for the problematic issue of requiring an integer result of
some computation, a real number can in general be substituted where an
integer is expected, and support all operations directly, whereas an
integer does not support the operation set of reals, because integers
are defined in terms of a limited operation set: the int has to first be
converted to the corresponding real number. The value subset relationship
is thus seen as a relationship between reals and the *corresponding*
integers converted to reals.

When this topic was discussed some ten years ago or so the term
*generalization* was used for the R-I relationship, as opposed to
OO *inheritance*. And, wild guess, I suppose generic programming
(templates) better supports expressing generalization than OO
inheritance does. But that's just a guess -- any comments?

Hth.,

- Alf

Dennis Yelle

unread,
Mar 15, 2001, 7:31:09 AM3/15/01
to
Dylan Nicholson wrote:
[...]

> Your belief is that the only valid use for
> public inheritance is to override virtual funtions (i.e. dynamic
> polymorphism) - some of us find it useful for a myriad of purposes. Part of
> the popularity, and arguably a lot of the strength of C++, comes from the
> fact that it can be used, safely and reasonably, in many different ways, for
> many different programming styles and paradigms. It is also, as is well
> evidenced by the discussions on this newsgroup, a healthy source of much
> heated debate!

Good. You are willing to take the other side of this argument.
Thanks.

Please give us a list of this "myriad of purposes" you
refer to above. I really want this list, I am not just out
to beat you over the head with it. My opinion is that most of
them will actually indicate flaws in the C++ language itself,
so, please provide a list and maybe it will lead to improvements
in the next version of C++.

Thanks.

Dennis Yelle
--
I am a computer programmer and I am looking for a job.
There is a link to my resume here:
http://table.jps.net/~vert/

Branimir Maksimovic

unread,
Mar 15, 2001, 9:30:52 AM3/15/01
to

"Eugene Karpachov" <j...@steel.orel.ru> wrote in message
news:slrn9atv...@localhost.localdomain...

> 13 Mar 2001 18:12:07 -0500 Branimir Maksimovic написал:
> >Question : Is natural number an integer?
> >No. Natural number is-a positive integer.
>
> You are making mess.
>
> Is positive integer an integer? Of course yes.

Yes :), that was mistake.

Actually that should look like this:

class Natural { /*.........*/ };

class Integer{ /*.........*/ };

class PositiveInteger: public Natural, public Integer { /* .............
*/ }

class NegativeInteger: public Integer { /* ............. */ }

But what about zero ?
It is not an integer number nor bellongs to N,
but is associated with set Z and sometimes even with N (No).
Mathematical set means that something bellongs to
set, not that something is-a set.
So my conclusion is that mathematical sets of numbers
cannot be represented as classes of objects, rather as
collections of non related types that have defined
conversion functions. Just it looks like that to me.
Main difference is that class represent set of similar objects,
but mathematical set can be union of two and more different
sets of unrelated types, so it pretty looks like multiple
inheritance case to me.

e.g. :

class Natural { };
class Zero { };
class InvNatural { };

class Z : public Natural, public Zero, public InvNatural {};

class N: public Natural {};

P.S. Just my thinking

Greetings, Bane.

Harri

unread,
Mar 15, 2001, 3:14:09 PM3/15/01
to
> >No. Natural number is-a positive integer.
>
> You are making mess.
>
> Is positive integer an integer? Of course yes.

No, positive integer is not an integer. You can't do everything to a


positive integer that you can to an integer. (I.E. assigning a negative
integer to it).

Francis Glassborow

unread,
Mar 15, 2001, 4:22:33 AM3/15/01
to
In article <98muaa$dlo$1...@nntp6.u.washington.edu>, Mary K. Kuhner
<mkku...@kingman.genetics.washington.edu> writes

>The sibling-group concept works better for me than thinking about
>whether Derived ISA Base. I think the natural-number example
>is a good illustration of why.

I think that this is a very useful way of expressing the concept of sub-
typing.


--
Francis Glassborow
See http://www.accu.org for details of The ACCU Spring Conference, 2001
(includes many regular participants to C & C++ newsgroups)

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Nicola Musatti

unread,
Mar 15, 2001, 4:09:30 AM3/15/01
to

"Mary K. Kuhner" wrote:
[...]

> What it is, is a relationship among *siblings*. If I have
> several classes inheriting publically from a common base,
> that's because I want to be able to write code that treats
> all the siblings as interchangable. If I don't intend to
> have a set of siblings, or I don't want to treat them
> interchangably, I would be better off not inheriting.

Well said! In other words inheritance is a way to explicitly express the


commonality among a given set of classes in order to be able to use them
interchangeably at runtime. This is in contrast, for instance, with
templates which are a way to exploit the implicit commonality among a
set of classes in order to use them interchangeably at compile time.

> The base class is not a thing. It's a compendium of
> the ways in which the siblings are meant to be interchangable,
> and an alias for an arbitrarily-chosen sibling.

[...]


> It seems to me that looking at it this way--emphasizing the
> sibling relationship, not the parent-child one--immediately
> rules out a lot of the dysfunctional inheritance patterns.

[...]


> In this view, a concrete base class is just an optimization
> in which we acknowledge that one of the siblings has no
> personality of its own beyond the interchangable parts, so
> we don't need to bother defining it separately. You can
> always work out such cases by considering them as siblings.
> Concrete inheritance is then proper only if one sibling is
> completely devoid of anything that wouldn't be an
> appropriate specification of the interchangability contract
> for the sibship.

I still think that a concrete base class, i.e. an instantiable one, is


very often a mistake. It is true on the other hand that the

identification of commonalities among classes is often connected with


the existence of operations which are not only conceptually common, but
which are performed in exactly the same way and for which it may be
worthwhile to factor together the implementation in the base class, even
though there are practical considerations which might lead to the
opposite conclusion.

> Alas, this does point out that interchangability is in the
> eye of the user code (just as Ostrich inheriting from Bird
> may be fine if we only need to model calorie intake, not
> flying). But one practically always needs to imagine
> something about the user code before defining a class
> heirarchy anyway.

Which is the whole point about software design and programming! I would


contend that most classification schemes tend to serve a purpose rather
than being absolute.

> The sibling-group concept works better for me than thinking about
> whether Derived ISA Base. I think the natural-number example
> is a good illustration of why.
>
> Mary Kuhner mkku...@genetics.washington.edu

^
No wonder you know more about inheritance than most of us :-)

Best regards,
Nicola Musatti

John Hickin

unread,
Mar 15, 2001, 4:30:41 AM3/15/01
to
Andrei Alexandrescu wrote:
>

>
> The polymorphic model of the world goes from general to specialized. For
> example, something that many OO enthusiasts don't understand is that in
> statically-type OO languages you shouldn't add public member functions when
> you derive. In this respect, N should inherit Z. But then, you're pretty
> much away from any benefits that type safety might bring you.
>

OK, I'll take the bait. I don't understand this at all.

I have in mind a Shape/Circle example where Circle specializes Shape.
Now certainly the radius of a Circle might be changeable and so I might
want to add setRadius(.) as a public member function. Such a function
certainly makes no sense on the base class Shape. So it would seem that
I must abandon inheritance.

Additionally, under this type of guideline, neither the programmer nor
the compiler should generate a copy constructor or a copy assignment
operator. So I can't get the effect of c.setRadius(r) by coding it as c
= Circle(r).

What am I not seeing properly here?


TIA, John.

Ken Hagan

unread,
Mar 15, 2001, 3:15:02 PM3/15/01
to
"Mary K. Kuhner" <mkku...@kingman.genetics.washington.edu> wrote...
>
> I'm really going to stick my neck out here ... inheritance

> as I've found it useful is not really a relationship
> between parent and child class at all. What it is, is a
> relationship among *siblings*.

Yes, I think this is a enlightening place to put the emphasis.
For one thing, it clarifies why inheritance of implementation
is generally not as good as using of abstract base classes (or
"interfaces", if you prefer).

Sadly, I doubt whether IS-INTERCHANGEABLE-WITH will catch on.

Francis Glassborow

unread,
Mar 17, 2001, 10:30:42 AM3/17/01
to
In article <98ri2r$qcv$1...@thebe.syd.dav.net.au>, Dylan Nicholson
<dy...@bigpond.net.au> writes

>These last two methods would probably be cases where I would be told to use
>private inheritance and manual inject the names from the base class, or
>containment and manually forward every method to the contained class. But
>why? The only reason for doing either of these is to prevent people
>treating my class as an object of the base class, and I agree, they
>shouldn't be allowed to. But I am not going to make an maintenance
>nightmare for myself in order to achieve this - I'm happy enough just to
>document the intended usage/limitations along with the class. Furthermore, I
>have never seen anyone run into a problem whereby they attempted to use a
>derived class polymorpically when it wasn't intended as such.

The problem is not programmers explicitly trying to use a derived object
that breaks LSP, but implicit usage when a function requiring the base
type is called with the derived type.

Though it adds nothing to the functionality of the language, I really
would like to have the ability to write:

class Y : X {
public:
using class X; // inject the declarations in X into Y
...
};
With the same rule about over-riders as applies to using declarations.
That, I think, would largely solve the problem of using inheritance of
implementation.


--
Francis Glassborow
See http://www.accu.org for details of The ACCU Spring Conference, 2001
(includes many regular participants to C & C++ newsgroups)

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Francis Glassborow

unread,
Mar 17, 2001, 10:33:44 AM3/17/01
to
In article <98r8iu$m01$1...@nnrp.atgi.net>, Victor Bazarov
<vAba...@dAnai.com> writes

>I think you're twisting the definition. The LSP says that
>"IS-A" relationship means that you can use the derived class
>anywhere the base class can be _used_. Can you use a positive
>integer where an integer is expected?

i-j;

is valid for all i and j in the set of integers it is not valid for the
set of positive integers. Now in a finitie arithmetic such as that
implemented by many computer languages, it is arguably valid (provides
defined behaviour) though not what most users expect.

e.g:

int main(){
unsigned int i = 3;
unsigned int j = 4;
int k = i-j;
if (k<0) cout << "How did that happen?";
else cout << "That was not what I expected.";
cout << " :)" << endl;
return 0;
}
>

--
Francis Glassborow
See http://www.accu.org for details of The ACCU Spring Conference, 2001
(includes many regular participants to C & C++ newsgroups)

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dennis Yelle

unread,
Mar 18, 2001, 9:11:46 AM3/18/01
to
Dylan Nicholson wrote:
[...]
[ Here are 4 reasons to use public inheritance: ]

> 1) Inherit of type information. Widely used in STL (eg binary_function,
> unary_function, iterator)
> 2) As a form of template typedef-ing (seen in recent gotw thread) - I accept
> this IS a language flaw.
> 3) Extending an existing concrete class, often one provided by a 3rd party
> library. I often find library classes provide 99% of the functionality I
> need, quite often have exactly the interface I need, but not quite all of
> the functionality.

[...]

> 4) As a "Quick and easy" method of making use of the functionality and
> interface of another class.

[...]



> Perhaps you're right, it is a flaw in the language and what we need is
> "public static" inheritance, whereby the all members are inherited
> publically, but you cannot automatically cast to the base class.

Would public static inheritance as you propose it
solve all of 1, 3, and 4 above?

Dennis Yelle
--
I am a computer programmer and I am looking for a job.
There is a link to my resume here:
http://table.jps.net/~vert/

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Bill Weston

unread,
Mar 19, 2001, 3:13:37 AM3/19/01
to
"Michael S. Terrazas" wrote:

> The problem is that mathematics does not speak in terms
> of types when it says that the set of natural numbers is a
> subset of the integers. The areas of math that are more
> closely realted to types would be groups, rings, etc. And
> when ring theory is being discussed, you talk of a set of
> elements with two operations that have specific relations.
> At that point there is no realtionship between (N, +, *) and
> (Z, +, *) [actually, as I recall, (N, +, *) isn't even a ring since
> every element of N does not have an inverse wrt +]
>
> <<snip>>


> > So Square : Rectangle is problematic, but interesting
> > whereas Manager : Employee is fine. And boring.
>

> Manager : Employee is also problematic when you try to
> apply it to the real world. Much better is Employee as a
> free-standing class with a role that the individual plays in
> the organization. Actually it's a very informative and
> useful example, especially in C++.
>

OK, so both my examples are poor applications of subclassing. Sorry :-(.
I just wanted to quote one where it is supposed to work, and was thinking of
Stroustrup's basic example in TCPL(3) rather than erm there is a more
complicated idiom in Coplien -- are you alluding to that?

I make the mistake of trying to fit everything with a bit of structure into an
inheritance hierarchy. Mea culpa.
I agree that it is not The Right Thing to define N:Z. And that my trick for
avoiding LSP for templated types is pants.
I wish
a) it were easier to model non-hierarchically-related types
b) people thinking in C++ would go easy on people thinking in problem domain
terms and not quote Square Is-(NOT)-A Rectangle as if it were something to be
proud of.
c) One could find a way of experimentally building up relationships between
types provisionally that would not need to be totally redone as The Right Thing
became clear, presumably from using The Provisionally Right Thing. I think I
am saying ''ouch, LSP is quite unforgiving, isn't it?'' Which I guess must be
in the FAQ so sorry for wasting bandwidth -- but I have a legitimate question
-- see below

What are the alternatives for expressing non-inheritance interrelationships
between classis in C++?
I guess something to do with templates, (hmmm, a bit vague :-)
or something to do with casting,
or maybe somethining to do with separating interface from implementation.
(maybe inheritance creeps back in here)

Simple question about using casting
('cos everyone says user defined conversions are the devils tools)

I think Stroustrup write s somewhere that no more than one user defined type
conversion per type conversion sequence is The Right Thing. Can't remember
where or why. Can think why but not how I might want to subvert this -- I want
to be able to initialize Complex instances with Real instances, which I want to
be able to initialize with Integer instances. OK so writing two
type-converting constructors for Complex is not going to kill me, but
theoretically there might be hundreds of types in my system, and having
conversions work transitively might save lots of thinking and typing and
debugging.

So how does one make conversions work transitively?
Is there some template magic?
like maybe ?exists type Via such that Convertible<From, Via>::true &&
Conertible<Via, To>::true ?

> The real problem is that most examples, such as this,
> use a layman's interpretation of math and try to apply
> C++ to it.


>
> > But but but sets and subsets most people have an intuitive grasp of.
> > And the recommended books *do* say something like ''think of public
> derivation
> > as is-a''.
> > But then it all goes horribly wrong. And we are supposed to grin and bear
> it?
> > ;-)
>

> The correct mathematical concepts for subclassing would
> be subgroups, subrings and subfields. None of which hold
> any intuition for the vast majority of people. Set theory
>

Two points here:
1)I am not afraid of the correct mathemaatical concepts --its sunday afternnoon
hack time!
Should I be afraid?
I would *love* to develop some (meta)C++ code that modelled
Z / TwoZ == bool
Or should the syntax be Z % 2 ?

2)How soon should one turn away from the whiteboard/textbook to the computer
and write a first hack at a component?
It seems like when writing C++ not until one really really understands
completely, or at least to post doctoral level. :-(
No wonder projects suffer delays!
Remeber the John von Neumann quote (he knew about both maffs and puters)
''Young man, in this game we never *understand* things, we merely get used to
them''

> doesn't talk of operations, other than element of, union,
> intersection, set subtraction and the like. None of these
> operations apply to the members of the set. All member
> operations are external to the set and are applied using an
> algebra or a calculus.
>

Good point. You have domain knowledge and C++ knowledge and respect is due.
I could mention Halmos' Naive Set Theory though -- which as I remember coexists
with real hard correct ZF or GB axiomatic set theory and is 'good enough' for
mathmos to use when they are not doing heavily set theoretic maffs.
(NB #include <techie_buzzwords> // for trying to esbalish maffs credibility
;-)
Seemed analogous to programming to interfaces. But ''proof by analogy is
fraud'' (all my knowledge comes from titles and chapter headings! :-)

>
> So, the upshot is, subclasses have more to do with the
> nature of the operations upon objects than on an initial
> appearance of a subset relationship amongst the
> sets to which the operations apply.
>

OK, so how does one express the structure of the operations? Remeber they can
be made first class objects as functors. Is it a cool thing to express the
operations in a hierarchy? (Bill leads with chin as usual. Must stop drinkng
at Sunday lunch :-)

Cheers && TIA,
Bill

Francis Glassborow

unread,
Mar 20, 2001, 12:47:50 PM3/20/01
to
In article <3AB4D5BA...@ntlworld.com>, Bill Weston
<w.we...@ntlworld.com> writes

>So how does one make conversions work transitively?
For user defined ones? Basically you cannot do so implicitly (thank
god).


--
Francis Glassborow
See http://www.accu.org for details of The ACCU Spring Conference, 2001
(includes many regular participants to C & C++ newsgroups)

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Mary K. Kuhner

unread,
Mar 20, 2001, 12:49:21 PM3/20/01
to
Bill Weston <w.we...@ntlworld.com> wrote:

>What are the alternatives for expressing non-inheritance interrelationships
>between classis in C++?

There's containment: class A has a class B member, or a
pointer or reference to a class B object. This has some nice
flexibilties compared to inheritance. It's easy to have more than
one contained member, for example, whereas I don't even want to
think about inheriting from something twice.

There's also non-containment linkage. A node has a pointer to its
previous and next nodes, that sort of thing. This is further
from inheritance but still expresses an interrelationship of sorts.

There's friendship.

There's the relationship between the static parts of a class and its
non-static instances. I finally found a practical use for this last
week, and I'm quite pleased with myself. (In a few weeks I will
know if I ought to be or not....)

There's the relationship between classes which can be used in the
same template. This can be a very minimal relationship: vector
asks only a few things from its template arguments.

There are contractual relationships: this class promises to be
Copy Constructable.

>OK so writing two
>type-converting constructors for Complex is not going to kill me, but
>theoretically there might be hundreds of types in my system, and having
>conversions work transitively might save lots of thinking and typing and
>debugging.

You will lose back all the thinking and (especially) debugging you
gained the first time you try to use an integer variable and
it comes out, twenty-seven user defined conversions later, as a
Macintosh Window Manager. At least, that seems to be the general
experience.

I have the impression that after an initial burst of excitement,
a lot of people have given up on user-defined conversions more
or less completely, on the grounds that type safety is too useful
to risk subverting it with an invisible conversion. Multiple
conversions would just increase the number of ways to shoot yourself
in the foot, probably exponentially.

Mary Kuhner mkku...@genetics.washington.edu

John J. Rusnak

unread,
Mar 20, 2001, 3:35:38 PM3/20/01
to
>
> I think you're twisting the definition. The LSP says that
> "IS-A" relationship means that you can use the derived class
> anywhere the base class can be _used_. Can you use a positive
> integer where an integer is expected? Yes, you can. Assignment
> is _not_ inherited and therefore you cannot put the behaviour of
> the class while something is assigned to it as a requirement for
> the "IS-A" relationship. After all I _can_ assign a negative
> integer to a positive integer. It doesn't mean that it will be
> the same (or that it will hold the negative value). It may lose
> the sign, it may throw an exception...
>
> Victor

GASP!

I saw this last statement of "throw an exception"!!!!!

It amazes me that someone would want to define the
behavior/implementation of a method, the sole purpose of which is to
throw an exception. This is poor software engineering. The purpose of a
class interface is to provide a contract to the client which the class
(and/or its subclasses) will make every attempt to fulfill. There may be
exceptional cases which cannot be handled, but this is why it is called
an "exception"; one should strive to define an interface for which
"exceptional" cases do not exist. To deliberately provide a contract
that will always be violated seems ludicrous.

Furthermore, by the nature of inheritance, a method of an inherited
class should always handle the same cases that its parent's method does
(provided its parent provides an implementation); it should not act to
make the parent's contract more restrictive.

-John

Andrei Alexandrescu

unread,
Mar 21, 2001, 6:21:33 PM3/21/01
to
"Mary K. Kuhner" <mkku...@kingman.genetics.washington.edu> wrote in message
news:995942$7gq$1...@nntp6.u.washington.edu...

> Bill Weston <w.we...@ntlworld.com> wrote:
>
> >What are the alternatives for expressing non-inheritance
interrelationships
> >between classis in C++?
>
> There's containment: class A has a class B member, or a
> pointer or reference to a class B object. This has some nice
> flexibilties compared to inheritance. It's easy to have more than
> one contained member, for example, whereas I don't even want to
> think about inheriting from something twice.

Ummmm... I'd say, this is more of a relationship between objects than a
relationship between types.

> There's the relationship between the static parts of a class and its
> non-static instances. I finally found a practical use for this last
> week, and I'm quite pleased with myself. (In a few weeks I will
> know if I ought to be or not....)

Ok, I'll bite: whaddaya mean?

> I have the impression that after an initial burst of excitement,
> a lot of people have given up on user-defined conversions more
> or less completely, on the grounds that type safety is too useful
> to risk subverting it with an invisible conversion.

I tend to partly agree with that, but it's more due to the way there are
implemented (and the heritage they must dovetail with) rather than their
being fundamentally flawed.


Andrei

Anthony Williams

unread,
Mar 21, 2001, 11:35:14 PM3/21/01
to
"Victor Bazarov" <vAba...@dAnai.com> wrote in message
news:98r8iu$m01$1...@nnrp.atgi.net...

> "Harri" <n...@email.sorry> wrote...
> > > >No. Natural number is-a positive integer.
> > >
> > > You are making mess.
> > >
> > > Is positive integer an integer? Of course yes.
> >
> > No, positive integer is not an integer. You can't do everything to a
> > positive integer that you can to an integer. (I.E. assigning a
> negative
> > integer to it).
>
> I think you're twisting the definition. The LSP says that
> "IS-A" relationship means that you can use the derived class
> anywhere the base class can be _used_. Can you use a positive
> integer where an integer is expected? Yes, you can. Assignment
> is _not_ inherited and therefore you cannot put the behaviour of
> the class while something is assigned to it as a requirement for
> the "IS-A" relationship. After all I _can_ assign a negative
> integer to a positive integer. It doesn't mean that it will be
> the same (or that it will hold the negative value). It may lose
> the sign, it may throw an exception...

Yuck!

If the base class defines the contract

a=b => a==b (1)

where a is a value of some base class A, and b is any value of a class B
(which may or may not be A), then for any derived class object D,

d=b => d==b (2)

Otherwise D does not satisfy the LSP with respect to A.

If A is an integer-holding-class, B is a negative-integer-holding-class,
then the contract (1) holds.

If you assign a negative integer to a positive-integer-holding class, there
is no way this invariant can hold, especially if you throw an exception!

Thus if D is a positive-integer-holding-class, the contract (2) does not
hold. Thus D does not obey the LSP wrt A.

Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optoelectronics
The opinions expressed in this message are not necessarily those of my
employer

Nicola Musatti

unread,
Mar 23, 2001, 10:39:23 PM3/23/01
to

James Kanze wrote:
>
> Nicola Musatti wrote:
[...]


> > Well said! In other words inheritance is a way to explicitly express
> > the commonality among a given set of classes in order to be able to
> > use them interchangeably at runtime. This is in contrast, for
> > instance, with templates which are a way to exploit the implicit
> > commonality among a set of classes in order to use them
> > interchangeably at compile time.
>
> I don't really like the term "commonality". It can mean too many
> things. Inheritance can be used for different purposes, but the
> different purposes are distinct, and each deserves a more exact term
> than simply commonality.

Commonality is the term used by James Coplien in his "Multiparadigm
Design for C++", where he describes an approach to software design one
step of which is the identification of the elements of commonality and
variability that can be found in your problem domain. An analysis of the
chosen programming language and associated tools should provide
knowledge of the ways your tools provide to deal with different kind of
commonalities and variabilities (e.g. those that can be fixed at compile
time, those that must be handled at runtime). These two analysis
activities should help in adopting the constructs that better match the
problem domain.

> Most of the time, for example, my base classes (abstract) define a
> contract (or an interface, if you prefer); what is common between the
> derived classes is that they implement that contract. It's not just
> any random commonality.
>
> Note that it often makes sense to have an abstract base class even if
> there is only one class which derives from it (for now, anyway). I
> find it logical to radically separate the contract (which cannot
> easily be changed without rework in all of the clients) from the
> implementation (which can be modified as often as you like). If
> nothing else, the separation effectively documents what is guaranteed,
> and what is only a accident of the current implementation.

This may also provide an alternative to the pimpl idiom, as the base
class already insulates the implementation of the subclass so that the
header file in which the subclass is declared will probably only need to
be included in a corresponding factory class source (which may *need*
knowledge about the subclass's actual members).



> > > The base class is not a thing. It's a compendium of the ways in
> > > which the siblings are meant to be interchangable, and an alias
> > > for an arbitrarily-chosen sibling.

That is, it's a way to express a specific form of commonality among the
derived classes. Note that I'm not using the term "commonality" in a
generic sense, but only in the context of a specific problem.

I'll give you an example: bank accounts and investment fund
subscriptions have in common a "balance" attribute. This is only a hint
that the use of a single construct to implement that attribute may be
useful. It turns out to be a commonality in Coplien's sense only when I
become aware that in order to provide a customer's net asset value I
have to sum the balance from his/her bank accounts and his fund
subscriptions.

[...]


> The best way to factor out common implementation details is USUALLY
> delegation, not inheritance. There are, however, exceptions; in such
> cases, private inheritance is generally to be preferred; it is likely
> in such cases that the functions which implement the commonality will
> also be protected. (A frequent reason for using inheritance in this
> case is that parts of the common functionality need to be customized
> according to the actual class.)

I'm aware of this, but I do think that there are exceptions, or at least
I did use inheritance of implementation in public base classes in
situations where it did not backfire on me; in general for this to work
it has to be strongly related to a stable property of the application
domain, one that would be very expensive to change for the customer's
organization.

[...]


> I think that any simple relationship based on vague commonality is
> bound to fail in the long run. I would insist that the exact
> relationship be specified: A implements the contract defined in B, or
> A is a customization of B, or ...

I think that it's even more important not to loose touch with the
outside world's entities and processes that our software models, and
that the hardest part of design is trying to follow the pattern of
rigidity and flexibility that is manifested by the problem domain.

Best regards,
Nicola Musatti

Nicola Musatti

unread,
Mar 23, 2001, 10:50:45 PM3/23/01
to

Francis Glassborow wrote:
>
> In article <3AAF9B60...@divalsim.it>, Nicola Musatti
> <obje...@divalsim.it> writes
> >I still think that a concrete base class, i.e. an instantiable one, is
> >very often a mistake. It is true on the other hand that the
> >identification of commonalities among classes is often connected with
> >the existence of operations which are not only conceptually common, but
> >which are performed in exactly the same way and for which it may be
> >worthwhile to factor together the implementation in the base class, even
> >though there are practical considerations which might lead to the
> >opposite conclusion.
>
> It strikes me that this factoring of commonality into a concrete base
> class might be assisted if we had a way to combine private inheritance
> (thereby suppressing the implicit derived to base conversion) with a
> class scope using directive (there is no such thing at the moment) to
> republish the base class interfaces en masse.

Note that I tend to use this approach with *public* base classes, and
then only if such a base class does provide a useful interface to
clients *and* there is a subset of the conceptually common functionality
provided by the subclasses that is actually the same functionality.

Private implementation inheritance tends to often to be the lazybone's
delegation. This could be limited if we had explicit support for
delegation in the language. In another message a while ago I proposed
something like:

struct A { int f() { return 5; } };
struct B { int g() { return 6; } };

struct C {
A a;
B * b;
using a;
using *b;
} c;

c.f(); // calls c.a.f();
c.g(); // calls c.b->g();

[...]


> See http://www.accu.org for details of The ACCU Spring Conference, 2001

See you in Oxford :-)

Best regards,
Nicola Musatti

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Francis Glassborow

unread,
Mar 24, 2001, 3:29:35 PM3/24/01
to
In article <3ABB246F...@divalsim.it>, Nicola Musatti
<obje...@divalsim.it> writes

>Private implementation inheritance tends to often to be the lazybone's
>delegation. This could be limited if we had explicit support for
>delegation in the language. In another message a while ago I proposed
>something like:
>
>struct A { int f() { return 5; } };
>struct B { int g() { return 6; } };
>
>struct C {
> A a;
> B * b;
> using a;
> using *b;
>} c;
>
>c.f(); // calls c.a.f();
>c.g(); // calls c.b->g();

I think we really want the same functionality. The problem I have (only
minor) is that your 'fix' is, I think, a rather larger extension than
mine and I am less certain about the consequences of such a change. You
are actually asking for some form of hidden using directive applied to
the interface of a subobject. For example, what happens if there are two
instances of A in C? Furthermore, you are exposing your implementation.
So am I but in a way that is, to me, more natural. Do you intend that
c.f() works even when 'a' is private?

Apart from dogma, what is so wrong about using private inheritance + a
using directive (assuming we allowed it) precisely when we want to
inherit the whole interface/implementation but do not want derived->base
conversions?


>
>[...]
>> See http://www.accu.org for details of The ACCU Spring Conference, 2001
>
>See you in Oxford :-)

I look forward to it.


--
Francis Glassborow


See http://www.accu.org for details of The ACCU Spring Conference, 2001

(includes many regular participants to C & C++ newsgroups)

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Nicola Musatti

unread,
Mar 26, 2001, 3:27:25 PM3/26/01
to

I must confess that I haven't really thought out the details of my
proposal and I realize that it is a dangerous shortcut. Probably its
main advantage is that it might succeed in silencing inheritance haters
:-)

Your solution does appear more natural.


> Apart from dogma, what is so wrong about using private inheritance + a
> using directive (assuming we allowed it) precisely when we want to
> inherit the whole interface/implementation but do not want derived->base
> conversions?

In my opinion, nothing at all.

Best regards,
Nicola Musatti

Dave Harris

unread,
Mar 27, 2001, 5:34:49 AM3/27/01
to
obje...@divalsim.it (Nicola Musatti) wrote (abridged):

> struct C {
> A a;
> B * b;
> using a;
> using *b;
> } c;

Have you read what Stroustrup said about delegation in D&E $12.7? His
syntax was:

struct C : public *b {
B *b;
};

He said users had problems with it, partly because functions in B can
not override the virtual functions of C. Are you saying your proposal
has the same limitation but you think it a virtue?

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Nicola Musatti

unread,
Mar 27, 2001, 10:30:12 AM3/27/01
to

Dave Harris wrote:
>
> obje...@divalsim.it (Nicola Musatti) wrote (abridged):
> > struct C {
> > A a;
> > B * b;
> > using a;
> > using *b;
> > } c;
>
> Have you read what Stroustrup said about delegation in D&E $12.7? His
> syntax was:
>
> struct C : public *b {
> B *b;
> };
>
> He said users had problems with it, partly because functions in B can
> not override the virtual functions of C. Are you saying your proposal
> has the same limitation but you think it a virtue?

Considering we're talking about delegation I would call it hiding rather
than overriding. One could choose a resolution rule (e.g. a member
function in the delegating class hides member functions with the same
name in the delegate) or declare malformed a program containing this
kind of ambiguity. We could also refine the notation to allow something
like:

using a.f;
using b->g;

There are other open issues, such as what happens when a delegate
pointer member is invalid or null. My only claim is that my solution is
better than using inheritance of implementation just to avoid writing
several times:

int C::f() const { return a.f(); }

Is it a *good* solution? I don't think I have the experience to judge.

Best regards,
Nicola Musatti

Mary K. Kuhner

unread,
Mar 30, 2001, 2:27:42 PM3/30/01
to
In article <999l2t$auoq$1...@ID-14036.news.dfncis.de>,

Andrei Alexandrescu <andre...@hotmail.com> wrote:
>"Mary K. Kuhner" <mkku...@kingman.genetics.washington.edu> wrote in message
>news:995942$7gq$1...@nntp6.u.washington.edu...

>> There's containment: class A has a class B member, or a


>> pointer or reference to a class B object.

>Ummmm... I'd say, this is more of a relationship between objects than a
>relationship between types.

I don't think so; if every A contains a B (directly or by owning pointer,
that's a detail) that expresses a pretty strong relationship between A
and B. But perhaps I'm not understanding what you mean by "type"
as opposed to "object".

Containment is not substitutable for template-interchangability or
public inheritance, certainly.

>> There's the relationship between the static parts of a class and its
>> non-static instances. I finally found a practical use for this last
>> week, and I'm quite pleased with myself. (In a few weeks I will
>> know if I ought to be or not....)

>Ok, I'll bite: whaddaya mean?

I have an annoying class which normally offers one interface, but
*after a certain point in the program* is allowed to offer a quite
different interface. It can't offer the different interface earlier, because
essential information (from user input) is unavailable. For a long
time I stewed over how to tell every instance of the class that
it was now allowed to offer the new interface (and with what
parameters). I experimented with having instances register
themselves, but it was massive overkill--this is a small, lightweight,
extremely common class, often passed by value.

My team objected strenuously to setting a global flag, and no wonder.
I'd have had to store the parameters globally too.

Then it suddenly occured to me that I could inform the static part of
the class that the new interface was now legal, and voila, all of the
instances of the class would know about it. I didn't have to involve
any other classes, nor track all the instances.

That's what I mean by saying that there is a relationship between
the static part of a class and the non-static part. I think of the static
part as a class archetype to which all instances have access. (In the
first object-oriented language I learned, a text-adventure programming
language, the archetypes had a literal existance as objects. Certain
programming bugs could put the player into the "room" where all
the class archetypes were stored, a surreal experience.)

>> I have the impression that after an initial burst of excitement,
>> a lot of people have given up on user-defined conversions more
>> or less completely, on the grounds that type safety is too useful
>> to risk subverting it with an invisible conversion.

>I tend to partly agree with that, but it's more due to the way there are
>implemented (and the heritage they must dovetail with) rather than their
>being fundamentally flawed.

Do you have an idea for a safer implementation? I'm inclined to think
that the basic idea of silently converting among types is intrinsically
risky. The silent conversions among numeric built-in types clobber
me quite regularly even though they have been the same since early
C days.

Mary Kuhner mkku...@genetics.washington.edu

0 new messages