TITLE: default values and overloading

(Newsgroups: comp.std.c++, 10 Feb 99)

ALEXANDRESCU: "Andrei Alexandrescu" <alexandrescua@micromodeling.com>

>>void foo(int   =1  ,int   =2  ,int=3);
>>void foo(double=1.0,double=2.0,int=3);
>>
>>>| foo(default, default, 4);


NARAN: Siemel Naran

>>Which foo(...) called?


GLASSBOROW: Francis Glassborow <francis@robinton.demon.co.uk>

>but the same question can be asked of:
>
>foo();
>
>You cannot default all the parameters (or even all the trailing
>parameters if the leading ones have a common sequence of types.


CLAMAGE: Steve Clamage <stephen.clamage@sun.com>

Here's a hypothetical example without those faults:

void foo(int    = 1,   double = 2.0, int);
void foo(double = 3.0, int    = 4  , int);

foo(default, default, 5); // which foo?

It's easy to say, "OK, that case is ambiguous, but we'll keep
the feature."

The real problem is that default parameters interact badly
with overloading, and proposals such as this one just make
it worse. IMHO, we don't really need more opportunities for
ambiguous function calls.

In addition, default arguments have their own sets of
problems. For one thing, the binding of default arguments
sometimes surprises programmers.
 extern int i;
 ...
 int foo(int = i); // default is the global i
 ...
 int bar()
 {
     int i = 3;
     foo(); // incorrectly thinks 3 is the default value
     ...
 }

For another, you can accidently have different defaults for the
same function in different files and get an unexpected result.
(To be fair, having different sets of overloaded function
declarations in different translation units can also cause surprises.)

It's worth noting that default parameters appeared originally
in C++ because function overloading at the time was difficult
to use and error-prone. You had to ensure that all declarations
for all overloads of a function appeared in the same sequence
in every translation unit. The invention of type-safe linkage
in 1989 ("name mangling") removed that difficulty.

You can usually get the same effect with function overloading
that you would otherwise get with default parameter values.
Simple example with "forwarding functions": Instead of
 int foo(int=1, double=2.0);
you can write
 int foo(int, double); // the "real" function
 inline int foo(int j)    { return foo(j, 2.0); }
 inline int foo(double d) { return foo(1, d);   }
 inline int foo()         { return foo(1, 2.0); }

This forwarding-function technique doesn't have the inline
advantage for virtual functions, and can't be used at all for
constructors. In those cases you have to repeat code or
call a common subroutine.

Lawrence Crowl has suggested the language be extended to allow 
"forwarding constructors".  Example:
 class T {
 T(int); // the "real" constructor
 T() : T(3) { } // forwarding constructor
This pair of constructors would have the effect of 
 T(int=3)
I don't see any problems with this extension, although one
compiler writer said he didn't see how to implement it. (When
this particular person can't see a way to implement it, I assume
I have missed something.) If feasible, it would go a long way to
elmininating the need for default arguments.
