How is this template specialization instantiated

99 Views Asked by At

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.

3

There are 3 best solutions below

2
Caleth On BEST ANSWER

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.

4
HolyBlackCat On

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?

Because the former is more specialized. When N < 0, the former is a specialization for Enable == true, and you pass true to Enable (implicitly via the default argument), which matches the specialization.

0
user12002570 On

The behavior of the program can be understood using partial ordering:

When a class or variable(since C++14) template is instantiated, and there are partial specializations available, the compiler has to decide if the primary template is going to be used or one of its partial specializations.

  • If only one specialization matches the template arguments, that specialization is used

(emphasis mine)

template<int N, bool Enable = true> struct S is a primary template while template<int N> struct S<N, N < 0> is a partial specialization. This partial specialization will be used when the first template parameter N is less than 0 which is why for N=-5 the partial specialization is used while for N=5 the primary template is used.

S<-5>::print() //this uses partial specialization S<N, N < 0>
S<5>::print() //this uses the primary template