Function template specialisation is a tricky beast — the short answer is, avoid it.
For my sins I’ve recently had to implement some fairly generic code in C++ using function templates, and I’ve realised something a little quirky about then that hadn’t occurred to be before.
For anybody who’s unaware of templating in C++ this post probably won’t be of much interest, but as a refresher of the syntax you can declare a class template like this:
1 2 3 4 5 6 7 8 |
|
Aside from having to define these classes in the header file (because the compiler requires the implementation of each method to be available to instantiate the template) this works more or less as you’d expect. You can also specialise templates, providing an alternate implementation for particular types:
1 2 3 4 5 6 7 8 9 10 11 |
|
This works really well for classes, but you can also template functions and methods in the same way:
1 2 3 4 5 |
|
And finally, you can also specialise them in the same way1:
1 2 3 4 5 6 7 8 |
|
So, that’s all there is to it right? Well, not quite. You see, function templates are slightly different beasts to class templates. For example, you can still overload templated functions just like you can with regular functions. The following are all quite valid at the same time:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Clearly there’s some potential for ambiguity here, and since this is all valid
syntax then the compiler can’t just give you a warning. The first rule is that
non-templated methods (the third one above) always take precedence over
templated ones. So, if myFunction()
is passed an int*
then the third method
above will always be called.
Essentially this means that the fourth method declaration will never be called,
right? Well, almost — you can actually force it to be invoked by calling it as
myFunction<int*>(...)
. But otherwise, the non-templated method is always
counted as a better match.
However, there is still potential for more confusion here. Let’s omit the non-templated method and reorder the definitions slightly2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Now we call myFunction()
with an int*
again. Since we’ve removed the
non-templated method, the specialisation will be called, right?
WRONG!3
Our simply re-ordering of the declarations means that the specialisation is now
an explicit specialisation of the first template, since the second didn’t exist
at the point it was defined. When the second template is added, it makes a
better match for int*
than the first template, so the specialisation of the
first template isn’t even considered.
The general rule of overloading templates like this is that only generic templates themselves overload on each other, specialisations aren’t considered until a base template has been chosen, even if there’s a specialisation which would have made a better match.
Confusing, eh? Still, at least there’s an easy moral lesson here — don’t specialise methods. In general you shouldn’t have to, since you can just overload them with non-templated methods which will take precedence anyway, which is probably what you wanted in the first place.
Or just avoid templates entirely. And C++. Use Python. Or just take a walk in the park instead. Look, there’s a bird making a nest. Hopefully out of the shredded remains of the C++11 standard.
Note that the <std::string>
suffix to the function name can only be ommitted if the compiler can unambiguously determine the template type. ↩
This code example is essentially one presented by Peter Dimov and Dave Abrahams — I found it in an article originally written about this which I’ve fairly shamelessly plagiarised here. ↩
I can’t really blame you for getting it wrong, however. Especially if you didn’t — it’s all a bit presumptuous of me, really, Let’s move on. ↩