Paavo Helde <
ees...@osa.pri.ee> wrote:
> Function overloading is great for simple utility functions where one
> immediately understands what it is expected to do, and it basically does
> the same thing in all overloads.
>
> Function overloading becomes anti-pattern when the overloads do
> different things. For example, there are processing stages where a
> function processes some parameters, then calls another function with the
> same name and partially processed parameters. This may create confusion,
> especially if I search the codebase for the function name and it is not
> immediately obvious which overload is called where. Better to add
> suffixes to the names like "stage1" and "stage2" if one cannot think of
> better names.
I have to deal in the past with production code bases where function
overloading was heavily abused (in more or less a manner of "I'm going
to overload this function name because I can", rather than it making sense),
and it made the code that used those overloaded functions extraordinarily
confusing to read.
For example, the code would be full of function calls of the form
"convert(a, b);" and that's it. Not very informative, plus there was like
nine billion overloaded versions of that function for different types of
first and second parameter, doing almost every possible thing under the
sun (eg. converting from an int, unsigned, long, unsigned long, float,
double, etc. to a std::string or to a std::wstring, with the corresponding
overloaded versions doing the conversion in the other direction. And that
was just one set of conversions. There were many more, including conversions
between string encodings. All named "convert()", with different types
and amounts of parameters.)
In fact, the very fact that the function name didn't even tell if it
was converting from the first parameter to the second or the other
way around made the code really obfuscated to read.
And the thing was, absolutely nowhere in the code was there any advantage
in the function being overloaded. In other words, at no point in the
codebase was there any sort of template or abstracted type that would
have benefited from that 'convert()' function being overloaded.
If I were to refactor all that code, I would create unique names for
all those functions, clearly stating the types and the direction of
conversion, like:
convert_to_int_from_charp(a, b);
convert_to_int_from_string(a, b);
convert_to_string_from_int(a, b);
and so on. If, and only if, the need appears for an overloaded version
of some of those functions, then they can be implemented *in addition*
to those more explicit names (the overloaded versions just calling the
more explicitly named versions), for example like:
void convert_to_value_from_string(int, const std::string&);
void convert_to_value_from_string(double, const std::string&);
etc. Only in the absolute extreme need would I resort to a completely
overloaded name that encompasses all those conversions. Even then I would
still use a name clearer than just 'convert()', even if it's just
something like 'convert_to_value_from_value()' (to indicate which
parameter is the destination and which is the source.)
(And no, I most definitely do not subscribe to the "brevity over clarity"
principle of programming. The more code written by other people I have
to deal with, the less I subscribe to the principle. When I have to read
other people's code, I really get an appreciation for clearly named
functions and variables, which are not only clearly named but also
follow a clear consistent naming pattern and convention, rather than
different patterns and conventions being wildly mixed, even within
the same source file.)
That being said (and as I mentioned in my other reply to this thread),
function overloading most definitely has its use, especially in
generic code.