[comp.std.c++] Co-ordinating the polymorphism in C++

craig@gpu.utcs.utoronto.ca (Craig Hubley) (02/16/91)

Since there was a recent post about this very issue in comp.std.c++, since
it's a question about the desirability of changing something in the language,
and a stream was already going on this subject here, I have cross-posted this
and directed followups to comp.std.c++, which I believe is the appropriate
forum for such discussions.

In article <DSOUZA.91Feb14151948@optima.cad.mcc.com> dsouza@optima.cad.mcc.com (Desmond Dsouza) writes:
>
>In article <600@taumet.com> steve@taumet.com (Stephen Clamage) writes:
>
>   craig@gpu.utcs.utoronto.ca (Craig Hubley) writes:
>
>   |Now that C++ has three polymorphism mechanisms
>   |	- overloading		(compile-time)
>   |	- templates		(link-time)
>   |	- virtual functions	(run-time)
>
>   |Is anyone thinking about how to reconcile them ?  As it stands now, I can
>   |use overloading to add extra arguments to functions, etc., but I can't do
>   |it in my derived classes because virtuals can't be overloaded, etc.

I stated this badly.  They can't be overloaded in the way I want, that is,
to allow pointers to derived classes as return values in place of pointers
to bases, and pointers to base classes as arguments in place of pointers
to derived, and have such an overload override the original function.
That is, I want the compiler to override the base class's function wherever
a derived function is defined that
	- accepts a parameter list convertible to one the base would accept
	- returns a value of a type convertible to one returned by base
my definition of "convertible" is quite restrictive here:  pointers to 
derived classes, which are legal substitutes for pointers to bases.  I am
interested in discussing expanding this definition to include other builtin
conversions, but I do not think it should extend to user-defined ones.
For one thing, the overriding rules should NOT depend on user-defined code,
which can change and cause unexpected chaos.  But the builtin conversions
will not change in this fashion, and are largely there to ease the pains of
dealing with the C data types, and to manage inheritance.

Other OO languages permit such type-compatible overloading, and in some
languages (e.g. Trellis) it is enforced.  That is, you can only define
changes that will leave a derived type a legal substitute for a base
type in all circumstances (i.e. type-safe).

>   Overloading need have no relation to polymorphism.
>   Templates and polymorphism are orthogonal concepts.
>   Virtual function may be overloaded.
>
>The rules for overloading resolution with class derivation is
>remarkably similar to the virtual function mechanism (in fact, it
>resembles 'multi-methods' with dispatch based on the derivation
>closeness of *all* arguments, instead of just the 'this' pointer),
>except the work is done at compile-time.

I suppose I am suggesting they ought to be one mechanism, not two.
Overloaded functions should also be permitted to return "more specialized"
types and accept "more general" arguments.

>Templates are the C++ implementation of what Cardelli called
>'parametric polymorphism'. You could say templates and class
>derivation/virtuals are orthogonal, except that template-instantiated
>classes could have meaningful class-derivation relationships between
>them. e.g. read-only-list<cars> Vs read-only-list<sports-cars>

Thanks for the reference.  To invoke Trellis again, its type-generators
(equivalent to templates) can be declared "not_flat" which simply means that 
the generated types (e.g. collections) have the same inheritance relationship
as the type used to generate them.

>Virtual functions may NOT be overloaded in the manner Craig seems to want.

Not at present, although I think a strong case can be made for the change.

>A derived class,D, with a virtual functions D::f whose arguments
>differ from those of its base class,B, HIDES the base class function,
>and does not override B::f.  Doing otherwise breaks type-safety. See

This is a rather narrow notion of type-safety.  I would argue that the
general theory does not apply here.  C++ recognizes distinct types but
also blurs the distinction between some types (e.g. pointers to base vs.
derived, numerical types) with its unique builtin type conversion.  This
causes some things of "different" type to be guaranteed to be treated as
if they were the same, so long as not more than one user-defined conversion
is invoked, and I am suggesting we leave them out of this completely.

In this case, the argument list may well be convertible to one that B::f
will accept, and with only builtin conversions being invoked.  There is no
change to any user program that will cause this conversion to be invalid,
thus this argument list is always a valid substitute for that of B::f.

>   craig@gpu.utcs.utoronto.ca (Craig Hubley) writes:
>
>	>To propose one small change, if virtuals could be overloaded in type-
>	>compatible ways (i.e. redefining acceptable rguments as pointers to base 
>	>classes in place of pointers to base classes, returning pointers to derived 
>	>classes in place of pointers to base classes, and permitting extended
>	>argument lists where defaults have been provided) this would add no more
>	>work to the compiler than a simple decision to exhaust matching on virtual
>	>functions before trying for an exact match at a different level, and no
>	>overhead at all to user programs that did not use these features.

>	>In effect it is no more than removing some arbitrary constraints on typing
>						    ^^^^^^^^^
>	>that are inconsistent with the rules in the rest of C++, which is exactly
>	>the kind of change we have been seeing between versions of C++.
>
>'Arbitrary' ? Thats kinda strong! Sometimes inconvenient, yes.

I will stick to my gadfly term.  Every other language that pretends to be OO
at least permits, usually encourages and sometimes enforces what I propose.
It arises from sound principles, and C++ is arbitrary in flouting them
without a clear reason that is stated in practical terms, not with abstract
terms like "type safety" which I have demonstrated mean something different
in C++ than in other languages.  Second, C++ itself permits data values of
these types to be freely substituted but somehow flinches at permitting
functions that return these types from being similarly substituted (i.e.
overloaded functions can't return the more specialized type).  Thus it is
inconsistent with itself.

>1 Redefining acceptable arguments to be pointers to derived classes
>  (I'm assuming thats what you meant) instead of to base classes will
>  break strong typing.

No, the opposite.  You can *generalize* arguments but not *specialize*
them without risking failures.  The derived virtual may try to use aspects
of the derived type it expects, when only the base type is there, since
that was what the original base virtual expected, and that was what
programmers provided.  That would be equivalent to automatically casting
B* to D*... which C++ doesn't do.

If the base class has a virtual thus:

class B {
	virtual B* foo(D*);
}

Then this is what we are talking about (comment from a previous post)

class D : public B { 	// D* is always demotable to B*, so the function call
	B* foo(B*) {};	// D::foo(D*) should be equivalent to D::foo((B*)D*)
			// It could also be interpreted as B::foo(D*), which
			// might be why it's illegal, but a simple rule to
			// exhaust argument matching on virtuals before trying
			// the base functions (if they are tried at all) would
			// resolve this ambiguity.
}

And this is what we were talking about above:

class E : public B {
	D* foo(B*) {};	// Similarly, any context that ends up calling
			// D::foo() where B::foo() is expected will 
			// receive a D* value that can be used as a B*.
			// But C++ says that overloads and virtuals
			// can't change the return type at all.  Why not
			// allow it where the objects are the same size
			// and C++ already has a built-in promotion ?
}

You are "doing the same" (D) or "doing more" (E) with "less information".
And here are the consequences:

// if all of the above were allowed, extending C++ consistently with the
// rules mentioned above yields the following results:
//
main {
	B* b; D* d; E* e;
	b->foo(b);	// illegal, fine, B* shouldn't automatically promote
	b->foo(d);	// legal, exact match, return B*
	d->foo(b);	// legal, exact match, return B*
	d->foo(d);	// legal, D::foo((B*)d), return B*, ignore B::foo(D*)
	e->foo(b);	// legal, exact match, return D*
	e->foo(d);	// legal, E::foo((B*)d), return D*, ignore B:foo(D*)
}

Whether there are any other consequences, is up to us to determine.  :)

>2 Returning pointers to derived classes in place of pointers to base
>  classes requires a change to function call/return sequences,
>  particularly if the implementation follows the cfront model of
>  laying out multiply-inherited classes and virtual tables. It can,
>  however, be done without compromising type safety.

Agreed.  I didn't expect it would be a picnic for compiler writers.
It is people using and writing code I am thinking about.  There
was an excellent post in comp.std.c++ showing the kind of problems that
are created by NOT having this mechanism.

>3 Extended argument lists in the derived class would definitely change
>  the function call sequence, since optional arguments are currently 
>  completely resolved at compile time.

We haven't really talked about the implications of allowing extended
lists.  One issue with such a change would be that calls to virtual
functions using the extended arguments are incompatible with the
original base function.  Perhaps the compiler should reject any
such call where the base (with the shorter argument list) is one of
the object's potential types.  In other words, it's only legal where
I declare, say, a D* or pointer to a type derived from D, but not
where I declare a B*.  It would be nice to have a way to test the
actual type, and do type-specific things in the case arm where the
type is guaranteed to match, but C++ doesn't allow programmers to
see the darn type tag at all.

>Note that you *can* have these three together:
>a.	B::f(B*)
>b. 	D::f(B*)
>c.	D::f(D*)
>
>But b) overrides a), while c) does not override a).

Yes.  This is fine.  But my point is given:

d.	B* B::f(D*)
e. 	B* D::f(D*)
f.	D* D::f(D*)
g.	D* D::f(B*)
h.	B* D::f(B*)

e) overrides d)
f) should be allowed and overrides d)	(compile error if e) already exists)
g) should be allowed and overrides d)	(compile error if e) or f) exists)
h) for completeness, should be allowed, overrides d), unless e)-g) exist)

Calls to d) plus the builtin conversion would invoke f)-h) correctly.
Values returned by f) and g) are converted by builtins to those
acceptable by any context originally calling d).

>1 -->  really boils down to the 
>	"contravariant Vs covariant"
>controversy. The contravariant rule, which guarantees strong typing,
>does NOT allow D::f(D*) to override B::f(B*). The covariant rule
>allows it, but can fail at run-time in some circumstances.

This isn't what I'm proposing, as I make clear above.  I'm against runtime 
failure in C++, and as stated above I don't think the general principles
of strong typing can be applied to C++ unless one deals explicitly with
the builtin conversions.

>I think some of these limitations can be avoided if the compiler is
>allowed to generate 'customized' code, like the Self compiler does.
>e.g. 2 versions of D::f() -- one to be called from a B* object, the
>other from a D* object (or multiple entry points) and emit some
>run-time type checks and type conversions in the B* version.

This is a possibility, I see no reason for C++'s compiler to always
default towards braindeath, and it satisifies the rule that it costs
nothing if you don't ever try to use this feature.  But as Bjarne
said, this would "bless contravariance".

>but thats a pretty major change :-)

I agree.  I don't see much reason for it.  C++ is designed to let you
do this sort of thing for yourself, to some degree, although testable
type tags are certainly required to do it right.  You just gotta SEE the
type of the object sometimes.

>Desmond D'Souza.
>
> Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324
> Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza

-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig

chip@tct.uucp (Chip Salzenberg) (02/18/91)

According to craig@gpu.utcs.utoronto.ca (Craig Hubley):
>I want the compiler to override the base class's function wherever
>a derived function is defined that
>	- accepts a parameter list convertible to one the base would accept
>	- returns a value of a type convertible to one returned by base
>my definition of "convertible" is quite restrictive here:  pointers to 
>derived classes, which are legal substitutes for pointers to bases.

In the abstract, I like this idea.

On the other hand, the fact that this rule has not existed in the
language to date means that currently unrelated functions, which are
separated perhaps by several layers of derivation, would suddenly
become alternative implementations of the same virtual function.

Also, member functions that are separately legal would suddenly
conflict with each other if they were both valid overrides for a
virtual function in a base class.

If C++ were still being designed, or were still in use only internally
to AT&T, this change might have a chance.  But I think it's too late.
Code that has already been written depends on all definitions of a
given virtual function being identical.  For the ANSI committee to
change this significant aspect of the language would be irresponsible.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
 "I want to mention that my opinions whether real or not are MY opinions."
             -- the inevitable William "Billy" Steinmetz

craig@gpu.utcs.utoronto.ca (Craig Hubley) (02/19/91)

In article <27BFE464.3FB9@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to craig@gpu.utcs.utoronto.ca (Craig Hubley):
>>I want the compiler to override the base class's function wherever
>>a derived function is defined that
>>	- accepts a parameter list convertible to one the base would accept
>>	- returns a value of a type convertible to one returned by base
>>my definition of "convertible" is quite restrictive here:  pointers to 
>>derived classes, which are legal substitutes for pointers to bases.
>
>In the abstract, I like this idea.

You're not the only one.  :)  In fact, if you consider that almost all OO
programming languages only let programmers deal with names/pointers/
references rather than "real" objects, and often treat "derived" types
as exact substitutes for "base" types, they have all already *implemented*
this idea.  Clearly, it doesn't present any insurmountable technical 
problems.  Which leaves the backward-compatibility problems:

>On the other hand, the fact that this rule has not existed in the
>language to date means that currently unrelated functions, which are
>separated perhaps by several layers of derivation, would suddenly
>become alternative implementations of the same virtual function.

It would be quite easy for tools like the eventual Clint to find this 
situation and warn about it, but the fact that it would break existing
code means that a more gradual approach need be adopted.  Perhaps a
different keyword than "virtual" for this type of override, or a 
qualifier to virtual (virtual++ ?  only kidding, maybe virtual* although
the * is overloaded enough).  Or even a compiler flag, which works in 
ANSI C for distinguishing old style definitions which would otherwise
be illegal.

>Also, member functions that are separately legal would suddenly
>conflict with each other if they were both valid overrides for a
>virtual function in a base class.

Same answer as above.  It should be easy to find such situations,
after all the compiler *has to* find them to build the override table.
Who needs Clint ?

>If C++ were still being designed, or were still in use only internally
>to AT&T, this change might have a chance.  But I think it's too late.

Perhaps.  But there are constructs in K&R C that go boom in ANSI C, too.
And one hell of a lot of people used K&R C.

>Code that has already been written depends on all definitions of a
>given virtual function being identical.  

But it is not "identical" in a semantic sense anyway.  Outputting "doctor"
rather than "nurse" is a different operation with different effects, 
even if the program can't see what they are.  And I don't believe that
very many people are overloading virtual functions anyway at the moment.
Because the rules are a mess, which was my original point.

One is reminded of the story about "make" where its designer decided not
to change its horrid syntax the DAY AFTER he gave it to TEN other people.
I mean, there is backwards compatibility, and then there is absurdity.

ANSI is supposed to represent the economic interests of the industry as a
whole.  Forcing thousands of programmers doing prototypes in other OOPLs
to bugger everything around to fit C++'s unique and (from what I can see)
less-powerful resolution of virtuals is a far larger ongoing expense to the 
software industry than a flag or extra keyword.  After all, when the words
"catch" and "template" were added as keywords it caused some code (probably
not much) which used these as variable names to break.  This was OK since
that was easy to spot and change.  This kind of problem is easy to spot and
change too:  a parser can flag every instance that would be different under
the old and new rules.  And my guess is, there won't be many.

>For the ANSI committee to
>change this significant aspect of the language would be irresponsible.

Only if they do it without providing a clear migration path wherein it is
possible to avoid code breaking.  I like the alternate or modified keyword
approach.  Then programmers themselves could choose which of the two means
of resolution they found most useful.  My expectation would be that
any programmer prototyping systems in another OOPL, or those that had
been taught the OO paradigm, or learned another OOPL first, like myself,
would prefer the more-liberal overriding paradigm.

>Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>


-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig

cok@islsun.Kodak.COM (David Cok) (02/19/91)

In article <27BFE464.3FB9@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to craig@gpu.utcs.utoronto.ca (Craig Hubley):
>>I want the compiler to override the base class's function wherever
>>a derived function is defined that
>>	- accepts a parameter list convertible to one the base would accept
>>	- returns a value of a type convertible to one returned by base
>>my definition of "convertible" is quite restrictive here:  pointers to 
>>derived classes, which are legal substitutes for pointers to bases.
>
>In the abstract, I like this idea.
>
>On the other hand, the fact that this rule has not existed in the
>language to date means that currently unrelated functions, which are
>separated perhaps by several layers of derivation, would suddenly
>become alternative implementations of the same virtual function.
>
>Also, member functions that are separately legal would suddenly
>conflict with each other if they were both valid overrides for a
>virtual function in a base class.
>
>If C++ were still being designed, or were still in use only internally
>to AT&T, this change might have a chance.  But I think it's too late.
>Code that has already been written depends on all definitions of a
>given virtual function being identical.  For the ANSI committee to
>change this significant aspect of the language would be irresponsible.

My original post which started part of this thread asked only for the
second of Craig's requests (including references as well as pointers) -- that
	Given base class B and derived class D and virtual functions
		virtual B* B::f();
		virtual B& B::g();

	that the derived class be able to supply its versions of these
	virtual functions as

		D* D::f(); // these currently illegal
		D& d::g();

	instead of
		B* D::f(); // these currently legal
		B& D::g();

Either (but not both at once) returning D* or returning B* would be legal.
Since returning D* is now illegal, no code would break or have its
semantics changed.

I realize that this proposal does not address all the concerns
of other posters (e.g. Craig Hubley) related to conversions on function
parameters.  However, in my (couple of years) of C++ use being able to do
what I propose for return types would have reduced the number of helper
functions and lines of code significantly (10s of percent), whereas the
restrictions on function argument types have rarely been a problem for me.
I'd be interested in real (I can invent by own) examples where allowing 
conversions on function arguments caused a significant simplification.

David R. Cok
Eastman Kodak Company  cok@Kodak.COM

chip@tct.uucp (Chip Salzenberg) (02/21/91)

According to craig@gpu.utcs.utoronto.ca (Craig Hubley):
>I want the compiler to override the base class's function wherever
>a derived function is defined that
>	- accepts a parameter list convertible to one the base would accept
>	- returns a value of a type convertible to one returned by base
>my definition of "convertible" is quite restrictive here:  pointers to 
>derived classes, which are legal substitutes for pointers to bases.
>
>Perhaps a different keyword than "virtual" for this type of override, or
>a qualifier to virtual (virtual++ ?  only kidding, maybe virtual* although
>the * is overloaded enough).

I didn't even think of the "virtual*" kind of language addition.
In that context, I could go for it.

>But there are constructs in K&R C that go boom in ANSI C, too.

Granted.  Try, catch and throw will make my programs go boom until I
change my variable names.

>>Code that has already been written depends on all definitions of a
>>given virtual function being identical.  
>
>But it is not "identical" in a semantic sense anyway.

What the captain meant to say was, "... being identically typed."

>Forcing thousands of programmers doing prototypes in other OOPLs to
>bugger everything around to fit C++'s unique and (from what I can see)
>less-powerful resolution of virtuals is a far larger ongoing expense to
>the software industry than a flag or extra keyword.

This statement belies an assumption that all OOPLs are, or should be,
semantically equivalent.  It ain't so, nor should it be: an approach
(OOP) does not a language make.  Smalltalk is a great environment, but
it's lousy for prototyping C++ programs.
-- 
Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>
"It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber
   (with reference to the upage bug in Interactive UNIX and Everex ESIX)

craig@gpu.utcs.utoronto.ca (Craig Hubley) (02/22/91)

In article <1991Feb19.120934.22963@kodak.kodak.com> cok@islsun.Kodak.COM (David Cok) writes:
>In article <27BFE464.3FB9@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>According to craig@gpu.utcs.utoronto.ca (Craig Hubley):
>>>I want the compiler to override the base class's function wherever
>>>a derived function is defined that
>>>	- accepts a parameter list convertible to one the base would accept
>>>	- returns a value of a type convertible to one returned by base
>>>my definition of "convertible" is quite restrictive here:  pointers to 
>>>derived classes, which are legal substitutes for pointers to bases.
>
>My original post which started part of this thread asked only for the
>second of Craig's requests (including references as well as pointers) -- that
>
>	... nice description omitted...
>
>Either (but not both at once) returning D* or returning B* would be legal.
>Since returning D* is now illegal, no code would break or have its
>semantics changed.
>
>I realize that this proposal does not address all the concerns
>of other posters (e.g. Craig Hubley) related to conversions on function
>parameters.  However, in my (couple of years) of C++ use being able to do

So far as I know I am the only one on the net at present proposing that these
conversion rules be extended explicitly to function parameters.  The language
already supports an implicit form of this, of course, by allowing conversions
to be invoked when calling a function.  I am arguing for it to be made
explicit.  My concern is that several levels down in the type matrix these
restrictions on optional arguments or on declaring the type of the argument
will become unacceptable, and I (or someone else) will have to create a new
function with a new name.  Thus a set of capabilities that could have been
one polymorphic function becomes two, and the complexity of knowing which
to call ends up on the programmer again.  This restricts reusability too,
since code that deals with the base types can't handle the derived types
lower down the hierarchy since the function name has changed.

>what I propose for return types would have reduced the number of helper
>functions and lines of code significantly (10s of percent), whereas the

I would agree with this assessment.

>restrictions on function argument types have rarely been a problem for me.

Probably because of the already-powerful conversion system.  I am only
proposing that this be explicitly recognized when compiling overloaded or
virtual functions.

>conversions on function arguments caused a significant simplification.

C++ already supports such conversion, and you can see good examples of the 
value of it (e.g. int to float) anywhere.  However, it doesn't let the
programmer declare or enforce any constraints.  If I was not allowed to
write a function to divide even ints and produce an int, then I would
always be invoking the float version at significant additional overhead.
It would also be confusing if int/int produced a float, which is the issue
we are addressing talking about return types.  Being able to tailor
based on the arguments can improve efficiency, but if C++ converts them
with a general conversion function (e.g. a constructor) so that a generalized
function can deal with them, this gain is lost.  It is more a question of
overhead than simplification in this case.


-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig

craig@gpu.utcs.utoronto.ca (Craig Hubley) (02/22/91)

In article <27C30630.523F@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>Forcing thousands of programmers doing prototypes in other OOPLs to
>>bugger everything around to fit C++'s unique and (from what I can see)
>>less-powerful resolution of virtuals is a far larger ongoing expense to
>>the software industry than a flag or extra keyword.
>
>This statement belies an assumption that all OOPLs are, or should be,
>semantically equivalent.  It ain't so, nor should it be: an approach

Not equivalent, just consistent where other languages with longer 
experience have proven the way to go.  The training and conversion
expense to the industry of C++'s "unique" approach requires explicit
justification, and I don't see it anywhere.

>(OOP) does not a language make.  Smalltalk is a great environment, but
>it's lousy for prototyping C++ programs.

With multiple inheritance and better interoperation with other languages,
it might be better for prototyping a C++ type hierarchy, though.  Except
that you would be managing all the strong typing yourself.  I am on the
fence about Smalltalk as a prototyping tool.  In prototyping I like
terrifyingly powerful tools like LISP/LOOPS (haven't done enough with CLOS)
that let me not only shoot myself in the foot, but microwave myself.


-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig

dsouza@optima.cad.mcc.com (Desmond Dsouza) (02/26/91)

craig@gpu.utcs.utoronto.ca (Craig Hubley) writes:

	>   >1 Redefining acceptable arguments to be pointers to derived classes
	>   >  (I'm assuming thats what you meant) instead of to base classes will
	>   >  break strong typing.
	>
	>   No, the opposite.  You can *generalize* arguments but not *specialize*
	>   them without risking failures.  The derived virtual may try to use aspects
	>   of the derived type it expects, when only the base type is there, since
	>   that was what the original base virtual expected, and that was what
	>   programmers provided.  That would be equivalent to automatically casting
	>   B* to D*... which C++ doesn't do.
	>

OK, what you want would be type safe (arguments are contravariant).

There was recently a long discussion on this in comp.object. Bertrand
Meyer claimed that few cases, if any, need a derived class to have
*more* general arguments than its base class, and used this to support
co-variance instead of contravariance. If you have convincing examples
which do need contravariance, could you post them?

Note also that implementing this could also mean change to function
call/return sequences, specially with the cfront implementation of
multiple-inheritance, where converting Derived to Base pointers
involves run-time checks and offsets. Multiple compiler-generated
versions of functions, or multiple entry points, can eliminate the
overhead of this when it is not used. 

Returning a pointer (reference) to a derived class instead of a base
class is definitely useful, though it has similar effects on
implementation efficiency in call/returns.

--
Desmond.
--

-------------------------------------------------------------------------------
 Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324
 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza

jimad@microsoft.UUCP (Jim ADCOCK) (02/27/91)

In article <1991Feb21.185106.20605@gpu.utcs.utoronto.ca> craig@gpu.utcs.utoronto.ca (Craig Hubley) writes:

|Not equivalent, just consistent where other languages with longer 
|experience have proven the way to go.  

If old languages *had* proven the right way to go, people wouldn't still
be deriving new languages today.  C++ already is saddled with restrictions
imposed by attempting to be compatible with one old language. 

Let's not add more!

craig@gpu.utcs.utoronto.ca (Craig Hubley) (03/03/91)

In article <70903@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>In article <1991Feb21.185106.20605@gpu.utcs.utoronto.ca> craig@gpu.utcs.utoronto.ca (Craig Hubley) writes:
>
>|Not equivalent, just consistent where other languages with longer 
>|experience have proven the way to go.  
>
>If old languages *had* proven the right way to go, people wouldn't still
>be deriving new languages today.  

I disagree.  No one language had all the answers, nor is it possible for
one language to make the "right" engineering tradeoffs for all people and
all applications.  We went through this in the 60s with PL/1, and in the
70s with Ada...

We gotta learn from our mistakes, too... and I don't believe that type
tags were originally in any of those other languages, they were added as
it was realized there were situations that required them.

People will be deriving new languages so long as there are new needs and
new technologies to meet them.  C++ is a response to a need for an efficient
data abstraction language.  Making it a good reusable code language ought
to fit somewhere between "efficiency" and "data abstraction" on the priority
scale.

>C++ already is saddled with restrictions
>imposed by attempting to be compatible with one old language. 
>
>Let's not add more!

If you mean "don't mimic the functionality of other languages", I agree,
there are enough problems supporting C already.  However, if a mechanism
from Smalltalk or whatever seems to work and solve a problem in C++, why
not mimic it ?  The alternative is to invent it over and over in practice,
training everyone how to do it, or worse, to invent it in a standards 
committee...

-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig