This is the homepage of Peter Alexander. I am currently at Facebook working on AR/VR. Any opinions are my own.
Recent Posts
- Single-Threaded Parallelism
- unique_ptr Type Erasure
- The Condenser Part 1
- Cube Vertex Numbering
- Range-Based Graph Search in D
All Posts
Links
github.com/Poita@Poita_
Atom Feed
Imagine C++ Without Function Overloading
I’ve spent quite a lot of time in the D programming language community, and one thing I’ve noticed is that people are very quick to suggest new features, but rarely do people suggest to remove a feature (unless it is outright broken). It’s easy to see the benefits of adding to a language, but the costs are often harder to see, or are simply ignored.
For a bit of fun, this post will explore the benefits of removing a useful feature from C++: function overloading.
Quick recap: function overloading is a feature of C++ that allows you to have two
functions with the same name that vary only on their parameters. In C, you would
to name your functions differently, e.g. with min
and fmin
.
In C++, you give them the same name, and the version is chosen based on parameters.
In C, you could use macros as a hacky form of overloading, and in C++ you could use templates, but I’ll ignore those for the moment.
One clear advantage to overloading is that you don’t need to name your functions
differently if they do the same things, but on different types. It makes things
easier to remember and arguably makes the intent of your code clearer. It also means
that you can change the types of your arguments (e.g. changing from float
to double
)
without having to go around changing all your function names. It can also help with
generic programming.
I’m sure there are other benefits, but those are the main ones. Now let’s go through the costs.
The first cost is the cost of name mangling. In C, since there can only be one function for a given name, the symbol assigned to that function is just the name of the function. In C++, two functions can have the same name, but they need to have different symbols, so C++ compilers must mangle the names to include the parameter type information.
Using g++, the two C++ min functions above mangle to __Z3minii
and __Z3minff
.
Those are quite simple examples. Things get more nasty when more intricate types
are involved. For example the std::sort
of an std::deque<std::pair<int,int>>
mangles to __ZSt4sortISt15_Deque_iteratorISt4pairIffERS2_PS2_EEvT_S6_
.
Why does this matter? It matters if you have to look at call stacks, or object dumps, or care about the size of your debug files. Many tools handle common C++ name mangling schemes, but this is extra work for the tool developers. It also doesn’t help that every compiler mangles names differently, which is why you can rarely link code generated by two different C++ compilers.
Without function overloading, there would be no need to mangle the parameter types into the symbol name.
Another cost of function overloading is the quality of compile time errors. If
I try to call min(1, 2.0f)
in C++, I will get an error because it cannot figure
out whether I want to call the int
version or the float
version. The only
way to express my intent is to cast one of the arguments min(1, (int)2.0f)
.
Without funcition overloading, if I want to call the int
version, I call min
,
and if I want to call the float
version, I call fmin
.
Having no direct way to specify what overload I want to use has other consequences. Suppose I want to get the address of one of the overloads because I want to use it as a parameter to a higher-order function.
How do you do it? &min
could refer to either the int
or float
version.
Do you know how to do it?
You have to cast the function pointer to the resolved overload function pointer type,
i.e. (int (*)(int, int))&min
gives you a pointer to the int
version. This would
be easier if the functions were named separately.
In the above scenario, things would be easier if min
were a template as you could
just refer to them as &min<int>
and &min<float>
. Just be careful when you mix
overloads and specialisation though, as they don’t play nicely with each other.
While we’re talking about things that don’t play nicely with each other. Have you ever tried to override a particular function overload in a derived class? That’s not simple either.
So, it would seem there are at least a few costs to overloading. I’m not sure if the costs are so great to warrant its removal from C++, but it’s worth thinking about these things every once in a while.