Reputation: 1038
Consider this code:
template<typename T, SomeEnum mode> struct TC{
T data;
//...
void doStuff();
};
can "doStuff" have more than one definitions based on the the enum value set for the template?
TC<int, SomeEnum::MODE_1> tc1; tc.doStuff(); //do some stuff
TC<int, SomeEnum::MODE_2> tc2; tc.doStuff(); //do some other stuff
(I don't mean save "mode" and make a branch on it but actually multiple definitions.)
Upvotes: 0
Views: 166
Reputation: 170153
You can do tag dispatch. Just provide an overload for each packaged value of the enum:
template<typename T, SomeEnum mode> struct TC{
T data;
//...
template<SomeEnum v>
using tag_type = std::integral_constant<SomeEnum, v>;
void reallyDoStuff(tag_type<SomeEnum::MODE_1>);
void reallyDoStuff(tag_type<SomeEnum::MODE_2>);
void doStuff() { reallyDoStuff(tag_type<mode>{}); }
};
Because the member functions of a class template won't be instantiated unless used, you'd only instantiate one definition of reallyDoStuff
(the proper one) for every instance of TC
.
When in doubt, prefer function template overloading to specialization. It's usually the superior alternative.
Upvotes: 4
Reputation: 118425
This is, in general, what template specialization is for. If you don't know what template specializations are, you need to read your C++ book first, before reading the rest of my answer.
The only stumbling block here is that individual class methods cannot be specialized, the entire class must be specialized. But there are common approaches around that, such as the following.
Define your member function as just a function call wrapper to a helper template class, like this:
template<typename T, SomeEnum mode> void TC::doStuff()
{
doStuff_helper<T, mode>::doStuff(*this);
}
That's your actual doStuff(). The actual code goes into the helper class. Define the helper class template as follows (you will need to properly use forward declarations, and other such miscellanea, of course):
template<typename T, SomeEnum mode> class doStuff_helper {
public:
static void doStuff(TC<T, mode> &me)
{
// ...
}
};
Everything that your original class method did, can now be done here, with some obvious differences. This not the actual method of the original class, any more. So, instead of the original this
, you have the me
reference here to use. And because this is not the actual class method, there will be the usual issues with accessing private or protected class members. But these are minor details that are easily solved on their own merits. The point is that what you can do now, is specialize the whole thing:
template<typename T> class doStuff_helper<T, MODE_VALUE> {
public:
static void doStuff(TC<T, MODE_VALUE> &me)
{
// ...
}
};
This doStuff()
can now be something completely different. This is the general approach for turning class methods specializations, which are not allowed, into ordinary, garden variety, class specialization.
There are further refinements on this general approach that are frequently used. One such refinement would be to have this factored out doStuff()
itself be nothing more than a wrapper and a method call to me
, with the general and the specialized versions invoking different methods in the original template class.
Once you then work out what happens here, with a piece of paper and a pencil, you will discover that what it ends up doing is turning a single call to the original doStuff()
class method into calling two different class methods (which would typically be private
), depending on the parameter to the original template class. And those two different class methods would essentially be your two different versions of doStuff()
that you wanted to have originally, with only the appropriate method being used depending on the template parameter.
Upvotes: 0