[comp.lang.c++] Reconciling overloading, virtual functions, conversion

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

I was wondering if anyone on the ANSI committee had given any thought to
reconciling the way virtual functions and overloading deal with polymorphism.
While virtual functions are supposed to provide the main means of substituting
objects of compatible types, in fact you can do more with overloading than with
virtuals, and so far I see no reason for this inconsistency.  The rest of this
is good fodder for language lawyers.

Virtual functions must match return type and order, number, and types of 
parameters.  However, while overloaded functions must match the return type,
they permit considerable flexibility in order and types of arguments.  
Meanwhile, C++ guarantees that a pointer to a derived object is an acceptable
(but not exact?) substitute for a pointer to a base object, where required.
And overloading of virtual functions is not permitted at all, presumably to
avoid confusing interaction of the two separate resolution mechanisms.  All
of these inconsistencies might add up to a sign that all of these mechanisms
might be better resolved into a single unambigous set of rules in ANSI C++.
This would involve relaxing some of the arbitrary constraints that exist now.

Consider the following, most of which are presently illegal, but are in fact
equivalents of very common constructs in other OOPLs.

class B {
	virtual B* foo(D*) {};	// I *said* they were going to be illegal,
				// didn't I ?  Only on a one-pass compiler.  :)
}

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.
}

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 ?
}

// 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*)
}

If any of you language lawyers can see a reason why this wouldn't work, or why
it *shouldn't*, please let me know.  As I see it, the two means of defining multiple
functions with the same name, virtuals and overloading, ought to be reconciled,
and strong typing relaxed just enough to allow true subtypes (derived objects
that are extensible in every ways compatible with contexts in which base objects
are used) to be defined.

In implementation terms, overloading a virtual function would normally lead
to the argument overloading being resolved at compile-time, and the virtual
function selection being resolved at run-time.  Any argument list that was
ambiguous (e.g. more than one of the overloaded functions might be called once
the complete argument list was known at runtime) would simply be another
possibility in the lookup table, albeit with slightly more overhead.  Trees
of classes that did not have any overloaded virtual functions, or just none
that survived until runtime, would not incur any extra overhead (a major C++
design goal, not to incur overhead unless a feature is used).

Comments and flames are welcome.  There may be some bugs in the above, since
obviously I can't compile it... yet.  :)

  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
  28 First Avenue, Toronto, Ontario M4M 1W8 Canada     Voice: (416) 466-4097

-- 
  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