c++language-lawyertemplate-specialization

How is this template specialization instantiated


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.


Solution

  • 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.