Both
function overloading and default arguments provide a convenience for calling
function names. It can seem confusing at times to know which technique to use.
For example, in the
BitVector
class it seems like the two
bits( )
functions could be combined into a single version:
int
bits(int sz = -1);
If
you called it without an argument, the function would check for the -1 default
and interpret that as meaning that you wanted it to tell you the current number
of bits. The use appears to be the same as the previous scheme. However, there
are a number of significant differences that jump out, or at least should make
you feel uncomfortable.
Inside
bits( )
you’ll have to do a conditional based on the value of the argument. If
you have to
look
for the default rather than treating it as an ordinary value, that should be a
clue that you will end up with two different functions inside one: one version
for the normal case, and one for the default. You might as well split it up
into two distinct function bodies and let the compiler do the selection. This
results in a slight increase in efficiency, because the extra argument
isn’t passed and the extra code for the conditional isn’t executed.
The slight efficiency increase for two functions could make a difference if you
call the function many times.
You
do lose something when you use a default argument in this case. First, the
default has to be something you wouldn’t ordinarily use, -1 in this case.
Now you can’t tell if a negative number is an accident or a default
substitution. Second, there’s only one return value with a single
function, so the compiler loses the information that was available for the
overloaded functions. Now, if you say
int
i = bv1.set(10);
the
compiler will accept it and no longer sees something that you, as the class
designer, might want, to be an error.
And
consider the plight of the user, always. Which design will make more sense to
users of your class as they peruse the header file? What does a default
argument of -1 suggest? Not much. The two separate functions are much clearer
because one takes a value and doesn’t return anything and the other
doesn’t take a value but returns something. Even without documentation,
it’s far easier to guess what the two different functions do.
As
a guideline, you shouldn’t use a default argument as a flag upon which to
conditionally execute code. You should instead break the function into two or
more overloaded functions if you can. A default argument should be a value you
would ordinarily put in that position. It’s a value that is more likely
to occur than all the rest, so users can generally ignore it or use it only if
they want to change it from the default value.
The
default argument is included to make function calls easier, especially when
those functions have many arguments with typical values. Not only is it much
easier to write the calls, it’s easier to read them, especially if the
class creator can order the arguments so the least-modified defaults appear
latest in the list.
An
especially important use of default arguments is when you start out with a
function with a set of arguments, and after it’s been used for a while
you discover you need to add arguments. By defaulting all the new arguments,
you ensure that all client code using the previous interface is not disturbed.