[comp.lang.c++] overloading inconsistent?

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

In article <27BFD8E3.3D1D@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to craig@gpu.utcs.utoronto.ca (Craig Hubley):
>>Overloading *is* a form of polymorphism, albeit a static one.
>
>I agree.

OK, so we agree that overloading is static.  :)

>>This is no different that building three types that inherit the same
>>virtual function.
>
>I disagree.  Overloading is a static phenomenon limited to compile
>time, and that fact is visible to the programmer.  For example, given

I plead guilty of posting to the wrong group: arguments about what C++
SHOULD do belong in comp.std.c++, not here.  But to answer, these are
no different in *principle*.  Annoyingly different in expression in C++, and
(I find) often intolerable in practice.  The fact that C++ implements the two
mechanisms so as to create (in my opinion) counter-intuitive effects
is (in my opinion) a flaw in C++.  That is, the programmer must be very
much aware of the "seam" between compile-time and run-time, and cannot
simply present a set of choices to the system, some of which can be made
at compile-time and others at run-time as the information is available.

It is that visibility to the programmer that is so disturbing.

Consider how static initialization works in C++.  The initialization
syntax means that some functions consist entirely of initialization
and calls to these can (in principle) be resolved entirely at compile-
time if they are called with static arguments.  That same function can
be used at run-time, and the programmer need not be aware of the 
difference.  This is very different than in C, which has no initialization
syntax, and forces the system to wait until run-time to resolve assignments.
It is a major advantage over C to be able to build one recursive data-
structure-building function and call it with static arguments.  In the bad
old C tradition, we would have been forced to build a set of initializations
or data file that would have had to be maintained separately from the
functions that add to the structure at run-time.  Or we could have bit the
bullet and waited 10 min. to initialize stuff at runtime, every time we fired
up our application.

Point being, it *is* possible to have one mechanism that is resolved at
both compile-time and run-time.

>a base class B and a derived class D:
>
>    extern void foo(B&);
>    extern void foo(D&);
>    D d;
>    B& b = d;
>    foo(b);
>
>It is the "foo(B&)" function that will be called, the decision being
>based on the _static_ type of the argument.  Likewise, given a
>non-virtual member function "foo()" defined in both B and D [no pun
>intended!]:
>
>    D d;
>    B& b = d;
>    b.foo();
>
>It is B::foo() that is called, this decision being made in a way very
>similar to the resolution of overloaded functions.

Yes, but it is the interaction of virtual functions and overloading
that presents the interesting problems.  In neither of the cases above
can the system be expected to read the programmer's mind and "decide"
to call the function associated with the "real" rather than the "formal"
type.  In fact, C++ explicitly uses the -> vs. . notation so that the
programmer can make that decision him/herself.  In your latter example,
you are *telling* the system to use the formal type.  In the former,
however, there is *no way* to tell the system to use the real type.
That is, there is no "cast to the dynamic type" operator in C++ that would
delay this decision until run-time.  You can't even find out what the type
is.

>In contrast, virtual function resolution is a _dynamic_ phenomenon.
>Were foo() made virtual, however, the decision between B::foo() or
>D::foo() -- or even some other, as yet unimagined, function E::foo()
>-- would be deferred until run time.
>
>So I conclude that it is NON-virtual member function resolution that
>correspond most closely to function overloading.

You have a good analogy there, but what you are proving is that overloading
is solving the same problem as member functions, but without the other half
of its brain:  runtime resolution.  When you mix virtuals and overloading,
things get scary, and in my opinion it's a direct result of this failing.
There is already a posting describing that in detail, so I won't repeat it.

>>>Templates and polymorphism are orthogonal concepts.
>>
>>Templates are closely related to inheritance in that they are both
>>ways to create a family of types with similar behavior.
>
>The preprocessor is also a means to creating a class family; yet it is
>obviously unrelated to inheritance per se.  As I understand templates,

It is just a way of building templates with macros, and is obviously
related in the way that I describe:  you can use it to create a family
of types with similar behavior.  

>it is true that classes may be defined in template form, but so may
>anything, including non-member functions that could otherwise be part
>of an old C program, e.g. a generic qsort() comparison function.  So
>templates and inheritance _are_ truly orthogonal.

You can use templates to do things that have nothing to do with
inheritance, they are in fact an *alternate* way to do some things.
For example, create a "protected" or "persistent" class without using
a mixin.  Fair enough.  But if "orthogonal" in this sense is supposed
to mean "never interact at all" you are wrong.  Consider a class that
I make with a template "Persistent".  Call it "PersistentEmployee".  Now
imagine I make another one:  "PersistentManager".  If Manager : public
Employee, does PersistentManager: public PersistentEmployee ?  Regardless
of which way language policy goes, you still have to make the decision.
Deciding "no, that's the job of inheritance" doesn't solve the problem,
it just ignores it.  And both are providing forms of polymorphism, which
you could call "type ignorance" for the user.  C++ templates are examples
of what is often called "parametrized polymorphism", whereas inheritance
provides "hierarchical polymorphism".

Templates, by the way, don't suffer from the problem discussed above:
Although you could build this kind of type at compile-time using the 
preprocessor before templates were added, you couldn't have extended
that syntax to deal with types to be determined at run time.  The template 
syntax would permit this, were it to be added to C++.

>>"Orthogonal" in the sense that they can be implemented separately, but
>>not in terms of how they interact to the user.
>
>Again, though, templates are a purely static phenomenon.  They are

The quick answer is "who cares?".  As a programmer, I couldn't care less
at what point in the compile/link/load/run process these things are
resolved, except where the language forces me to be aware of it.
Which in C++ is annoyingly often.  I am not *misunderstanding* how
this works, I am *disagreeing* with it.  Again, I apologize for not
having started this in comp.std.c++.  Followups redirected to there.

>utterly unlike virtual functions, which are resolved at run time.

Your sense of "utterly unlike" seems to be that of a compiler designer,
which probably represents .0001% of C++ users, and dropping rapidly.
Obviously you have to be aware of these subtle differences sometimes but
inventing wholly different syntax for each situation is IMHO a mistake.
And the situation could be improved by letting overloading, virtuals, and
builtin conversion interact consistent with the object-oriented paradigm.
Which, at present, they don't.

I suppose the problem starts with Bjarne who wrote his first book for
compiler writers not programmers.  In *most* languages, if one includes
the many commercial 4GLs, compile-time/run-time distinctions are hidden
from the programmer.  Similarly for the OOPLs.  True OOPLs tend to treat
everything as if it were potentially to be resolved at runtime, but then 
prunes the possibility tree at compile-time.  In other words, true OOPLs
have optimizing compilers, not inconsistent compile vs. run-time syntax.
But that is a theological matter. :)

>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