Yes, you can use this trick:
template <typename T, T, T=0> struct Foo {
};
template <typename T, T t> struct Foo <T, t, t> {
};
If t is 0 in the specialization, it will match the default argument, and the specialization is taken. Otherwise, the primary template is taken.
Edit: What the heck does the third parameter mean? Well, it's a default and it's 0. It will be passed when we name the specialization Foo<int, 5>
for example. But really, we instantiate a template with the arguments Foo<int, 5, 0>
, because the last is a default argument. The partial specialization matches, when the third parameter matches the third argument, which is zero by default, and if the third and second arguments are the same, because both are t
.
The above trick has the drawback that also Foo<int, 9, 9>
uses our specialization. But on the other side, the above is remarkable simple, so that you can probably get away with that. If you don't want that to work, then you can use enable_if
, which is a bit more complicated:
template <typename T, T, typename = void> struct Foo {
};
template <typename T, T t>
struct Foo <T, t, typename boost::enable_if_c< t == 0 >::type> {
};
Now, even if you say Foo<int, 9, void>
, our partial specialization won't be chosen, because the condition t == 0
isn't true, and ::type
will thus not be available. SFINAE doesn't chose the specialization then. Of course, with this enable_if solution, you are not limited to t being zero. Any condition will do. For reference, here is the code of enable_if, if you don't use boost. Cut the _c
suffix above then, which we don't need for the version below:
template<bool C, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> { };