"The C++ Programming Language", special edition, by Bjarne Stroustrup.
On page 312, he defines polymorphism *only* in terms of dynmic binding
and virtual functions.
I'm not sure if the special edition has the same page numbering, but
I'd guess so, because my 3rd Ed also talks about polymorphism on page
312. What's said is:
Getting "the right" behaviour from Employee's functions
independently of exactly what kind of Employee is actually used is
called polymorphism. A type with virtual functions is called a
polymorphic type. To get polymorphic behaviour in C++, the member
functions called must be virtual and objects must be manipulated
through pointers or references. When manipulating an object directly
(rather than through a pointer or refreence), its exact type is known
by the compiler so that run-time polymorphism is not needed.
Now, you'll notice that the first sentence defines polymorphism as the
getting of "the right" behaviour independently of exact types. It
explicitly doesn't yet descend into run-time polymorphic specific
techniques. "A type with virtual functions is called a polymorphic
type". This is true, but notice that he explicitly doesn't say "A
polymorphic type...", nor "Only a type with virtual functions is called
a polymorphic type". His next sentence is less carefully worded
(paraphrased slightly) "to get polymorphic behaviour member functions
must be virtual...", which is ambiguous, but he is after all in the
context of introducing virtual dispatch. He will have actually meant
that "to get [this type] of polymorphic behaviour member functions must
be virtual...".
If you look at the reasoning you've documented him using:
"both allow an algorithm to be expressed once and applied to a
variety of types, (so) people sometimes refer to both as polymorphic"
You should be able to see why operator overloading and indeed
preprocessor macros are polymorphic mechanisms. Consider that while
you do have to write explicit overloaded functions (no polymorphism
benefit yet), having done so you can have the same code dispatch a call
to whichever function is actually appropriate to the types passed. In
other words, it's the calling code that becomes polymorphic through the
support of the overloaded functions, and not the overloaded functions
themselves. For example, a preprocessor macro may say:
#define FN(X) do { if (!(X)) cerr << "!" #X " " << (X) << endl; }
while (false)
(Yes I know it's not side-effect safe). FN() is however polymorphic as
it can be used on different types of expressions (e.g. FN(my_int);
FN(3.14 * 2); FN("hello")
. But in order for FN to provide it's
behaviour, it's actually had to rely on a second source of
source-code-level polymorphism: the operator<< operator being available
for it's argument. Because of the overloaded streaming operators, this
code can be used unchanged for a set of supported types.
As for the distinction you're reaching for between overloading and
templates: templates can be automatically instantiated (preprocessor
macros have a similar benefit), but think back to virtual methods. We
know virtual functions implement a form of polymorphism, and yet you
can only substitute the finite set of classes you've actually bothered
to define. Like overloading, you can extend that set as necessary, but
it's still a manual process.
Anyway, it's not for us to "come up with a single term". The terms
already exist and have well-defined meanings.
For what it's worth, I also know plenty of people who don't understand
these distinctions (and plenty more who do). As you say, some would
and do ridicule people for suggesting that macros or overloading have
so much in common with their beloved OO virtual dispatch. Once or
twice they've been colleagues coming back from interviews sniggering
about the candidate who said otherwise... :-(. Luckily, I've never
been interviewed by someone similarly misinformed.
Cheers,
Tony