In the programming project I'm working on in my spare time, I'm using some C++ templates. I generally like templates; I think they can express some ideas elegantly. Their compiler error messages kind of suck, though, as we'll see shortly.
But this isn't about compiler error messages, exactly. This is about partial template specializations. Let's recap quickly with template total specializations first. When you have a templated class or function, you can add override methods for specific template values that do something other than the generic behavior. An example would be best.
#include <windows.h>
#include <stdio.h>
template<unsigned int NumLegs>
class Insect
{
public:
virtual void PrintInfo();
};
template<unsigned int NumLegs>
void Insect<NumLegs>::PrintInfo()
{
printf("An insect with %u legs\n", NumLegs);
}
template<>
void Insect<8>::PrintInfo()
{
printf("A spider!\n");
}
int __cdecl wmain(int argc, wchar_t* argv[])
{
Insect<6> ant;
ant.PrintInfo();
// output: An insect with 6 legs
Insect<8> roseHairTarantula;
roseHairTarantula.PrintInfo();
// output: A spider!
return 0;
}
So we have a way of declaring a generic insect and printing some information about it. But if someone has an 8-legged insect, a different implementation, one that takes into account the number of legs, nicely prints out the fact that we have a spider. Since all template arguments were used in the overridden PrintInfo
, this is called a "template total specialization".
What if we add another template argument? Say for some reason we want to track the gender of the insect as well as the number of legs? It might look something like this:
template<unsigned int NumLegs, bool IsFemale>
class Insect
{
public:
virtual void PrintInfo();
};
const char* GenderString(bool isFemale)
{
return (isFemale ? "female" : "male");
}
template<unsigned int NumLegs, bool IsFemale>
void Insect<NumLegs, IsFemale>::PrintInfo()
{
printf("A %s insect with %u legs\n", GenderString(IsFemale), NumLegs);
}
And, like before, we'd like to specialize the PrintInfo
function for spiders. Let's go for it in the obvious way!
template<bool IsFemale>
void Insect<8, IsFemale>::PrintInfo()
{
printf("A %s spider\n", GenderString(IsFemale));
}
This seems like the obvious thing to do. Take out the template parameters that we're fixing (8 legs) and leave the others. Too bad if you try this, you'll end up with:
main.cpp(29) : error C3860: template argument list following class template name must list parameters in the order used in template parameter list
And a bunch of other nasty, unintuitive errors. It turns out that if you want to do a template partial specialization, you have to re-declare the entire class and re-implement all the methods. What the... that makes them kind of dumb. Well, in lieu of re-declaring, maybe we can (ab)use inheritance?
template<bool IsFemale>
class Spider : public Insect<8, IsFemale>
{
public:
// re-declare methods that are overridden for this specialization
void PrintInfo();
};
template<bool IsFemale>
void Spider<IsFemale>::PrintInfo()
{
printf("A %s spider\n", GenderString(IsFemale));
}
So I've inherited from Insect
with the partial specialization in place, and I re-declare and override the methods that are changed for this specialization only. I think this technique is a suitable compromise that's not too inconvenient. A little tedious, but not too bad.
Note that in this case I used public inheritance because a spider actually is a type of insect. Concievably, I could have used private inheritance if I didn't want to express that kind of relationship.
And finally, the main
method and output:
int __cdecl wmain(int argc, wchar_t* argv[])
{
Insect<6, true> ant;
ant.PrintInfo();
// output: A female insect with 6 legs
Insect<8, false> roseHairTarantula;
roseHairTarantula.PrintInfo();
// output: A male insect with 8 legs
Spider<true> thaiZebraTarantula;
thaiZebraTarantula.PrintInfo();
// output: A female spider
return 0;
}
1 comments:
thanks sir! how's life these days?
-upal
Post a Comment