Jonathan Boccara's blog

Can You Wield C++ Function Overloading Like Jon Snow Wields Longclaw?

Published July 18, 2017 - 15 Comments

Game of Thrones C++ challengeAfter a long wait, the first episode of season 7 of Game of Thrones has come out.

It lasted for 59 minutes, and now we have to wait again for the second episode. To fill this void I’m proposing you a C++ challenge to keep ourselves busy until then!

This challenge will let you experiment with the language, and with function overloading in particular.

The missing link between the STL and function overloading

The STL is a fantastic tool to make your code more expressive and more robust. If you’re a C++ developer and want to become proficient, it is essential that you learn the STL.

But there is one case where we can’t apply STL algorithms right out of the box: when the function passed has overloads.

Here is an example to illustrate. Let’s consider this function f that takes an int by reference and adds 1 to it:

void f(int& i)
{
    ++i;
}

Now we use this function in the simplest algorithm, std::for_each, to increment every element of a container of numbers:

std::vector<int> numbers = {1, 2, 3, 4, 5};
std::for_each(begin(numbers), end(numbers), f);

So far, so good. But now let me just add a new function, that is also called f but that takes a std::string. In other terms, an overload of f:

void f(std::string& s);

I’m not even defining this overload, a mere declaration is enough to… cause a compilation error!

Overloads are perfectly legal in general in C++, but here the new overload prevents the call to the algorithm from compiling.

As if all the brutal deaths in Game of Thrones weren’t enough, our compilation dies with a last breath that whispers:

ain.cpp: In function 'int main()':
main.cpp:20:50: error: no matching function for call to 'for_each(std::vector<int>::iterator, std::vector<int>::iterator, <unresolved overloaded function type>)'
     std::for_each(begin(numbers), end(numbers), f);
                                                  ^
In file included from /usr/local/include/c++/7.1.0/algorithm:62:0,
                 from main.cpp:1:
/usr/local/include/c++/7.1.0/bits/stl_algo.h:3878:5: note: candidate: template<class _IIter, class _Funct> _Funct std::for_each(_IIter, _IIter, _Funct)
     for_each(_InputIterator __first, _InputIterator __last, _Function __f)
     ^~~~~~~~
/usr/local/include/c++/7.1.0/bits/stl_algo.h:3878:5: note:   template argument deduction/substitution failed:
main.cpp:20:50: note:   couldn't deduce template parameter '_Funct'
     std::for_each(begin(numbers), end(numbers), f);
                                                  ^

It’s a curious way of saying this: there are two f, so I can’t decide which one the algorithm should use.

That’s from a compiler’s point of view. From a human point of view the situation is obvious: there is one overload that takes ints, one that takes strings, and the collection contains ints. It’s a no-brainer, we should use the first overload of f, the one that takes ints.

One way to go about it is to say this explicitly at call site, with a static_cast:

std::for_each(begin(numbers), end(numbers), static_cast<void(*)(int&)>(f));

This disambiguates, and the code compiles. But it has several drawbacks:

  • it forces us to specify the return type (here void) even though it is not used in overload resolution,
  • it forces us to specify the exact const and reference qualifiers otherwise it won’t compile,
  • frankly, it’s an ugly mouthful of code to express very little.

Since it is a no-brainer for us to pick the right one, there must be a better way to go about this. A way that is more expressive.

Your challenge is to find this better way.

The challenge is now closed! You can go see the results.

The challenge

Feeling up to the challenge? Here is how to submit your solution:

  • You have to submit before the 29th of July 2017 at midnight, GMT. So the 29th is OK but the 30th is too late.
  • I’ll publish the name of the winner and a solution on Fluent C++ a few days later.
  • Start off from these test cases on Coliru, make them compile and make your code as clear as possible.
  • Write your name and email at the top of your solution (or send your address to me by email if you prefer). I’ll make sure to add you to Fluent C++ mailing list so that you’re notified when the results of the challenge come out (just say so if you don’t want me to, no hard feelings, but just think about logging in in 2 weeks to see the results)
  • Post the link of your solution (generate it with the Share! button on Coliru) in the comment section of this article,
  • If two people submit an equivalent solution, the one who submitted first wins.

Good luck, have fun, and in case you need anything I’m here to help.

Winter is here. And so is C++.

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin

Comments are closed