Sometimes Keeping The Type is Cheaper

Things have been quiet1 around here as of late. They’ll continue to be that way until CppCon as I’m preparing a talk and that’s keeping me too busy to do much posting at the moment2. If you’re going to be at CppCon and want to meet up feel free to use the email link over there on the right to get in touch. I always wonder who’s reading this stuff, and meeting one of them in person, while it would probably be weird, would also be cool.

While I am busy, apparently I’m not too busy for a someone is wrong on the internet post (that’s what you’re getting here). I came across this via the isocpp news feed. I like the idea a lot but have a small nit-pick with the implementation of the foreign constructor:

Font(std::function<void (Font)> visit)
{
visit(*this);
}

I’ve fixed the constructor argument so that it has the required template parameters. And while I prefer my code to compile, the missing template parameters isn’t my issue. Instead, it’s the use of std::function in this context. std::function has to do type-erasure on the thing that is passed to it and the price for that is memory allocation and a virtual function call. The memory allocation might be avoided if your std::function implementation does small value optimization and said optimization is applicable to what you’re storing in the std::function. The virtual function will prevent inlining which we probably want to happen in this case.

This removes the use of std::function and along with it the memory allocation and virtual function call. Now one thing to worry about with templates is code bloat, you’re going to get one instance of the Font constructor for each visitor type that you call it with. In this case that shouldn’t be a problem as I would be really surprised if the constructor didn’t get inlined eliminating the bloat.

[Update: I changed the argument to Font to be a forwarding reference. This seems to be the emerging consensus on how to take arguments like this since we avoid copying the function object and can accept both const and non-const function objects.]

In this case std::function was the wrong tool to reach for. So when is it appropriate to use std::function? When you need to store the function for later. In that case we don’t want to worry about the actual type that was passed in, just that we can call it with the given argument types and get the appropriate type returned. That’s what std::function was designed for. In the case above, while we don’t care about visitor’s type, it is actually more expensive to abstract the type away so we may as well keep it around.