C ++ Partial Template Specialization: Member Functions
I have a template
template <int a, int b>
class MyTemplateClass
{
// ....
void computeSomething();
};
which I would like to highlight partially for two special cases b:
template<int a>
void MyTemplateClass<a, 2> :: computeSomething()
{
// Special case for b=2 here
}
template<int a>
void MyTemplateClass<a, 3> :: computeSomething()
{
// Special case for b=3 here
}
However, as far as I know, partial specialization is not valid for member functions. How can I achieve what I want? Is there any other solution? Thank!
source to share
Possible way is to estract compute()
, create a base class for it, and specialize that base class.
I mean ... if you create a generic version and two specializations for fooSub
template <int a, int b>
struct fooSub
{
void compute ()
{ std::cout << "- foo generic compute()" << std::endl; }
};
template <int a>
struct fooSub<a, 2>
{
void compute ()
{ std::cout << "- foo compute() for 2" << std::endl; }
};
template <int a>
struct fooSub<a, 3>
{
void compute ()
{ std::cout << "- foo compute() for 3" << std::endl; }
};
you can "specialize" compute in foo
via inheritance just like this
template <int a, int b>
struct foo : public fooSub<a, b>
{ };
Another possible solution, if you can use at least C ++ 11, activate / deactivate another version compute()
using SFINAE ( std::enable_if
) as in the following bar
class
template <int a, int b>
struct bar
{
template <int bb = b>
typename std::enable_if<(b == bb) && (b != 2) && (b != 3)>::type
compute ()
{ std::cout << "- bar generic compute()" << std::endl; }
template <int bb = b>
typename std::enable_if<(b == bb) && (b == 2)>::type compute ()
{ std::cout << "- bar compute() for 2" << std::endl; }
template <int bb = b>
typename std::enable_if<(b == bb) && (b == 3)>::type compute ()
{ std::cout << "- bar compute() for 3" << std::endl; }
};
Runs a complete compiled example for both ways.
#include <iostream>
#include <type_traits>
template <int a, int b>
struct fooSub
{
void compute ()
{ std::cout << "- foo generic compute()" << std::endl; }
};
template <int a>
struct fooSub<a, 2>
{
void compute ()
{ std::cout << "- foo compute() for 2" << std::endl; }
};
template <int a>
struct fooSub<a, 3>
{
void compute ()
{ std::cout << "- foo compute() for 3" << std::endl; }
};
template <int a, int b>
struct foo : public fooSub<a, b>
{ };
template <int a, int b>
struct bar
{
template <int bb = b>
typename std::enable_if<(b == bb) && (b != 2) && (b != 3)>::type
compute ()
{ std::cout << "- bar generic compute()" << std::endl; }
template <int bb = b>
typename std::enable_if<(b == bb) && (b == 2)>::type compute ()
{ std::cout << "- bar compute() for 2" << std::endl; }
template <int bb = b>
typename std::enable_if<(b == bb) && (b == 3)>::type compute ()
{ std::cout << "- bar compute() for 3" << std::endl; }
};
int main()
{
foo<0, 1>{}.compute(); // print - foo generic compute()
foo<1, 2>{}.compute(); // print - foo compute() for 2
foo<2, 3>{}.compute(); // print - foo compute() for 3
bar<2, 1>{}.compute(); // print - bar generic compute()
bar<3, 2>{}.compute(); // print - bar compute() for 2
bar<4, 3>{}.compute(); // print - bar compute() for 3
}
source to share
Since the name of the template is known, you can add some implementation tool to the function computeSomething
to fork the stream, for example:
template <int a, int b>
class MyTemplateClass
{
// ....
void computeSomething()
{
if (b == 2) { computeWith2(); }
else if (b == 3) { computeWith3(); }
else { computerNormally(); }
};
}
source to share
One approach is to extract the calculations from this class into a separate one.
Then you can only specialize in this class of computation:
template <int a, int b>
class MyTemplateClass
{
// ....
void computeSomething() {
Computation<a, b> c;
c.compute();
}
};
template <int a, int b>
struct Computation { void compute () {} };
template <int a>
struct Computation<a, 2> { void compute () {} };
template <int a>
struct Computation<a, 3> { void compute () {} };
Though IMO it is better not to use specialization, but instead different (descriptive!) Names and compile times to choose between:
template<bool Condition,
typename Then,
typename Else>
using if_t = typename std:: conditional<
Condition, Then, Else>:: type;
template <int a, int b>
class MyTemplateClass
{
// ....
using Computation =
if_t<b == 2,
B2Comp<a>,
if_t<b == 3,
B3Comp<a>,
DefaultComp<a, b> > >;
void computeSomething() {
Computation c;
c.compute();
}
};
// Add (template) classes, B3Comp and DefaultComp
If you can already try C ++ 17, then the above can be rewritten to:
template <int a, int b>
class MyTemplateClass
{
// ....
void computeSomething() {
if constexpr (b == 2) {
B2Comp<a> comp;
comp.compute();
} else if constexpr (b == 3) {
B3Comp<a> comp;
comp.compute();
} else {
DefaultComp<a, b> comp;
comp.compute();
// Or just put the code here, if it short
}
}
};
Instead of template classes, you can also use template functions.
As opposed to using normal, if it avoids evaluating "unnecessary" code paths, which allows otherwise poorly formed code to be placed there (for example, recursively creating one template).
source to share