I was cobbling together some template recursion to construct a simple compile time integer range and ended up with this
#include <iostream>
template<int N, bool Enable = true>
struct S{
static void print(){
std::cout << N << ", ";
S<N - 1>::print();
}
};
template<int N>
struct S<N, N < 0>{
static void print(){
std::cout << N << ", ";
S<N + 1>::print();
}
};
template<>
struct S<0>{
static void print(){
std::cout << "0.\n";
}
};
int main() {
S<-5>::print();
S<5>::print();
}
The output is
-5, -4, -3, -2, -1, 0.
5, 4, 3, 2, 1, 0.
I expected this to work, but when I look at it I can't explain why its working. I've been perusing the cppreference Partial template specialization topic, I think the section on "members of partial specializations" hold some explanation that I'm unable to decipher. cppinsights just gives me a description of the observed behavior.
So why is
template<int N>
static void S<N, N < 0>::print()
preferred over
template<int N, bool Enable = true>
static void S<N, Enable>::print()
When N < 0
? The same is observed when print
is a non-static member, as I expect it to be.
The primary template is by-definition less specialised than any specialisations.
When expanding the uses S<N + 1>
and S<N - 1>
, N < 0
is evaluated and that specialisation is applicable if it matches the value true
, which comes from the default in the primary template.
N < 0
is only true when N is strictly negative, and that specialisation counts up. Once it reaches 0
, that specialisation isn't applicable, but the S<0>
one is.
Because the recursive uses don't supply the second template parameter, even if you supply false explicitly things sort themselves out.