#11. Don't use syntactic overloading

Syntactic overloading (or function overloading) is a C++ feature which allows a class to have several functions with the same name, but with different parameters. To say "Don't use syntactic overloading" is the same as to say "Don't give the same name to different functions".

class C
{
    ...
    void add(int i);
    void add(double d);
    void add(C other);
    ....
}

When you write this client code:

C c;
c.add(x);

The compiler will call the appropiate version of the funcion add, depending on the type of x.

This is what syntactic overloading is. It is called syntactic overloading to distinguish it from semantic overloading 8more properly called dynamic binding), which is the one that happens when you use inheritance and polymorphism.

Syntactic overloading may seem useful at first sight, but it has lots of drawbacks for no real advantage. It just can go wrong in too many ways.

Code is more readable without syntactic overloading. One name for each thing, different names for different things. Not using it keeps the principle of least surprise.

In the 2001 article I reference below, Bertrand Meyer explains:
Enforcing a one-to-one correspondence keeps everything simple and manageable. It makes the class readable and avoids confusion: if you see a feature name f and do not know what it means, you only have to search until the first feature declaration with that name, and stop there; you know that you have found what you are looking for, and do not need to worry about some competing definition. Easy, simple, and comforting.
Imagine what can go wrong if you don't follow this simple rule and insist in giving the same name to different functions. I'll give only three hindsights about that.

First, even in the absence of inheritance, syntactic overloading can lead to surprises when implicit type conversion comes into the picture. In the example above, if you call add exactly with an integer, a double or an instance of C, the compiled program should behave with no unpleasant surprises. But what if you call it with an unsigned integer? A float? A char? In some cases your code will not compile, but in others it will make an implicit conversion - and the choice of which one to make will not always be obvious.

Things get worse when inheritance comes into the picture. If each function has a totally different name in all the inheritance tree, except when you give the same exact signature (including the name, but also the parameter names and types) to a virtual function because you are redefining it in a derived class, you'll never wonder which function is being called. Any deviation from that good practice will eventually lead to confusion and trouble.

A third factor can make the picture even worse: default parameter values. In general, I'll advice against them. However, if they are used in a context of strictly no syntactic overloading, they won't be able to do much harm to the readability of your code. Mix the function name repetition of syntactic overloading with inheritance and default parameter values, though, and soon your client code will become a nightmare.

Bibliography

[Meyer 1997]
Bertrand Meyer: Object-Oriented Software Construction, 2nd Edition, Prentice Hall, 1997.

Meyer gives strong advice against syntactic overloading, refering to the principle of non-deception: "differences in semantics should be reflected by differences in the text of the software"; and explains why he decided not to include it in the language he designed, Eiffel. See pages 93-95.

See also the article Overloading vs. Object Technology, Bertrand Meyer, Journal of Object-Oriented Programming, October/November 2001.

Comments

Popular posts from this blog

#2. Make all type conversions explicit

Welcome to The Deep Blue C++

#13. Organize your code in classes