[comp.lang.c++] OVERLOADING + CLASS DERIVATION = PROBLEM

dsouza@mcc.com (10/19/89)

When resolving an overloaded function, based on Lippman, p 160:
	"Closeness of type is not considered."
it seems that the number of type conversions required for a match is
not considered in resolving a call. I understand this as a safety
feature with silent built-in conversions. However, I dont think it
fits in well with class derivation, as the philosophy and purpose of
automatic conversions in a type hierarchy is QUITE different. Maybe the
2 kinds of conversions should be distinguished? I hear "aargh!"s, but 
think about it for a minute. Here is an example:


Consider:
class X;
class A {};
class B : public A {};
class C : public B {};  // C subTypeOf B subTypeOf A

	// now for some overloading:
	//  I've added the X argument only so you wont say: Use A::foo member function

void foo (X, A) { ... } // [foo1] fine: if I did not define foo(A,B),
			//		this WOULD apply to (X,B) and (X,C)

void foo (X, B) { ... } // [foo2] fine: SHOULDN'T THIS APPLY TO AN  (X, C) pair?

main () { 
A a; B b; C c; X x;
foo(x,a);	// FINE: Perfect match with [foo1]
foo(x,b);	// FINE: Perfect match with [foo2] overrides 1 conversion match with [foo1]
foo(x,c);	// ERROR: 2 matches: 1 conversion -> [foo2], 2 conversions -> [foo1]
}

I'd like to make the following argument against this behaviour, based on some of the professed 
goals of overloaded functions and inheritance (and my interpretations of them :-)


1. To dispatch to different functions based on the static (dynamic)
type of the first argument use non-virtual (virtual) member functions. For dynamic
dispatch, the types have to be related by class derivation.

	[1a] One can view this as associating the specialized function directly
	with what triggered the specialization: i.e. the new class.

	[1b] This permits logically keeping the function definitions with the class
	definition, the lexical convenience of using the same function name
	for "similar" functions, and for virtuals, further eliminates nasty looking
	run-time tag-based switch statements.

2. To dispatch to different functions based on the static type of
class arguments (or non-class args) other than the first, use
overloaded functions. e.g. you may have different implementations
of SHOW_OBJ_IN_VIEW (OBJ,VIEW) for different class pairs: 
<OBJ1,VIEW1> , <OBJ1,VIEW2> , <OBJ2,VIEW1> etc.

	[2a] One can view this as associating the different implementations with
	what triggered the difference: i.e. COMBINATIONS of argument classes, or
	those arguments which are discriminnated on by the compiler to resolve calls.

	[2b] This permits the lexical convenience of using the same
	function name for "similar meaning" functions with
	differing implementations.

3. One of the PURPOSES of class derivation (public derivation) is to
extend/specialize the behaviour of functions for the BASE class in the
DERIVED class.

	[3a] One can view this as associating the specialized function directly
	with what triggered the specialization: i.e. the new class.

	[3b] The inheritance permits most behaviour to be automatically
	defined for the new class, and the specialized behaviour
	is associated where is logically belongs.
	

Therefore: as a logical extension of these same principles: 
	If CLASS DERIVATION allow behaviour to be inherited by DEFAULT, and
	OVERLOADED FUNCTIONS allow functions to discriminate (specialize) on the 
	types of more than one argument (ok, so its limited to compile time) : 
-->
Why not allow OVERLOADED functions to (effectively) do INHERITANCE, by 
discriminating on the class derivation hierarchy? 

I know that by ARTIFICIALLY breaking up the overloaded function "foo" into
pieces, and putting those pieces into MEMBER FUNCTIONS, I can fake
this, but this would violate several of the principles [1a], [1b], ...
[3b] above. Ditto for casts: What I'm suggesting is perfectly type safe and 
just refines ambiguity resolution.

Furthermore, overloaded functions (multiple discriminants) and inheritance (automatic 
conversions), in combination seem designed for just this.

COMPATIBILITY NOTE:
Extending overloaded function resolution to consider "Closeness" of
type match should not break any working code: it will simply successfully
resolve some calls which would previously be flagged as ambiguous.

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