I used to rely on pattern matching a lot which made me prone to this limitation of the language very often, for which I never found a elegant practical solution.
I guess I instinctively learned to steer away from this kind of design, although I still stumble upon it once in while.
There are four solutions that I know:
- The modern solution (
if constexpr
), (practical but not elegant IMO).
- The classical solution (not practical in real world).
- The classical solution + CRTP (practical but elegant?)
- The "esoteric" solution,
enable_if
(not elegant IMO)
This post elaborates mostly about number 3.
None of these solution are 100% satisfying to me.
I don't see a fundamental reason why the language couldn't just specialize the template class when it encounters a specialized member function declaration, at least with limitations.
The modern solution (shown in one answer) is to use if constexpr
in the general version.
I find this like cheating and it is not elegant and I agree with you that there must be a way to do this within templates.
The classical solution of course is what it is shown in the other answers too.
However I think it is not practical, the reason is that MyClass<N, 3>
typically looks very similar in its declarations and implementation to the general case MyClass<N, d>
therefore one has to repeat the whole implementation of MyClass
for each case (partially) specialized.
This is unacceptable to me.
For example, consider what happens if you have a more complex class:
template<class N, int d>
class MyClass
{
public:
void A() const{....}
void B() const{....}
.
.
.
void Z() const{....}
void doo(){std::cout << "general";}
};
Not necessarily, but chances are that you will need to also have a lot of repeated code:
template<class N>
class MyClass<N, 3>
{
void A() const{....}
void B() const{....}
.
.
.
void Z() const{....}
void doo();
};
I cannot think a worst case of DRY (Don't repeat yourself) violation.
The solution I found is to extract the absolute common parts of MyClass
:
template<class MyClassCRTP>
class BasicMyClass<MyClassCRTP> // eventually we need will need to know the derived class
{
public:
void A() const{....}
void B() const{....}
.
.
.
void Z() const{....}
};
template<class N, int d>
class MyClass : BasicMyClass<MyClass<N, d>>
{
public:
void doo(){std::cout << "general";}
};
template<class N>
class MyClass<N, 3> : BasicMyClass<MyClass<N, 3>>
{
void doo();
};
And now you can specialize MyClass<N, 3>::doo
without much repeated code.
Is this really elegant? I don't know.
It does also open other cans of worms.
Not ideal certainly, we wanted to specialize a (method) function and we ended up with extra classes!
Finally, for completeness, the esoteric solution, not better than if constexpr
but somewhat backward compatible IMO:
template<class N, int d>
class MyClass
{
public:
template<class Dummy, std::enable_if<d != 3 and sizeof(Dummy*), int> = 0>
void doo(){std::cout << "general";}
template<class Dummy, std::enable_if<d == 3 and sizeof(Dummy*), int> = 0>
void doo(){std::cout << "special";}
};