A comparative look at overloading in C++ - Part 1

Dec 6, 2014 • Paul Fultz II

There are different techniques for overloading on type requirements in C++. Lets take a look at comparing the different techniques. We’ll start by writing a simple implementation of std::advance, which increments an iterator by user-specified elements. However, if the iterator has random access traversal, it can be incremented in one step. Also, the iterator can advance backwards as well. So we need overloads for the three different iterator traversals. Lets use Tick to implement the traits for the these traversals:

We don’t use the iterator category since it is not always related to traversal(ie an input iterator could have random access traversal). So to test our advance function we can test it against std::vector and std::list:

Of course, negating the overloads can be a little difficult. Plus, it is not really maintainable. If a new overload needed to be added than the other overloads would possibly need to be negated as well.

Tag-dispatching

Tag dispatching works by creating tags that uses inheritance to order the functions:

Now, this is no better then the previous solution. However, this logic only needs to be written once, not for every function. Even so, this solution has other disadvantages. First, the dispatching boilerplate such as advance and advance_impl has to be written every time. Secondly, as it is currently written, it produces longer and more confusing compiler errors. Although this can be fixed, this extra work is required every time we want to do tag dispatching.

Of course, this is simpler than the previous solutions, however, it suffers from similar disadvantages: adding new overloads requires reordering the ranking, dispatching boilerplate is required every time, and it produces longer compiler errors.

Conditional Overloading

Conditional overloading allows us to order the functions, by picking the first function that is valid. We can use the conditional adaptor from Fit(or you can implement it yourself here):

This has several advantages over the other solutions. First, additional overloads can be added easily. Secondly, it doesn’t produce long and confusing compile errors. Thirdly, it doesn’t require a lot of boilerplate every time.

The disadvantage to this over tag dispatching is the functions have to be ordered by their relationships for every function rather than being handle by the tag hierarchy once. Additionally, if generic lambdas can’t be used then function objects need to be used instead, which can be a little more boilerplate as well without some clever macros.