-1
#include <iostream>
#include <utility>

using namespace std;

template <size_t N, typename V = int> // works if typename V = void
struct T
{
    const static int size = 0;
};

template <size_t N>
struct T<N,typename std::enable_if<(N>10)>::type>
{
   const static int size = 1;
};

int main (){
    cout << T<9>::size << endl; // 0
    cout << T<19>::size << endl;// 0  WHY?
    cout << T<10>::size << endl; //0  
}

Not sure why the output is the way it is, why isn't the specialization getting picked up?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
  • 2
    Int != void, and enable_if resolves to void when the arg is true. Change primary template to `typename V = void` – Chris Beck Apr 09 '19 at 19:22
  • 1
    Seems like a dupe of https://stackoverflow.com/questions/44858395/why-is-the-template-specialization-not-chosen/44858464 – Rakete1111 Apr 09 '19 at 19:25
  • 1
    Since you already noticed that it only works with `V = void`, can you explain why you think `V = int` should work? – HolyBlackCat Apr 09 '19 at 19:26

2 Answers2

1

Right now, the second argument is always int, since it's the default value:

int main (){
    cout << T<9, int>::size << endl;
    cout << T<19, int>::size << endl;
    cout << T<10, int>::size << endl;
}

But the expression typename std::enable_if<(N>10)>::type won't ever yield an int, so your specialization won't be picked. The default type for std::enable_if::type is void.

It will work only if you're sending void in the second argument (of course you don't want that):

int main (){
    cout << T<9, void>::size << endl;
    cout << T<19, void>::size << endl;
    cout << T<10, void>::size << endl; 
}

To make it work like you want, you must either make the default argument to be void, or make your constraint to always yield an int type.

template <size_t N, typename V = void>
struct T
{
    const static int size = 0;
};

Or alternatively make your constrain yeild the type of your default argument:

typename std::enable_if<(N>10), int>::type

In fact, you could put any type and it wouldn't change a thing. As long as the two type matches:

// silly but works, the two types are the same.
template<size_t N, typename = decltype(std::cout)>
struct T { /* ... */ };

template <size_t N>
struct T<N, typename std::enable_if<(N>10), decltype(std::cout)>::type> { /* ... */ };
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
0

Due to the template parameter with default argument in your primary template, when you write T<19>::size, what you're really writing is T<19, int>::size.

Now, there exist partial specializations for your template, thus, when figuring out which specialization a simple-template-id like T<19> refers to, the compiler will try to find a matching partial specialization. To do so, it will look for a match between the actual template arguments 19, int and the template arguments given in your partial specialization

template <size_t N>
struct T<N, typename std::enable_if<(N>10)>::type>
{     // ^-------------- this here -------------^
    …
};

The first step is to figure out the arguments for the parameters of your partial specialization itself. In your case, the partial specialization has one parameter template <size_t N>. This parameter is deduced by comparing the parameterized type T<N, typename std::enable_if<(N>10)>::type> to the actual type T<19, int>. Deduction yields 19 as argument for parameter N. Substituting back into the specialization, we get

struct T<N, void>

as the deduced argument list for our specialization because std::enable_if<true>::type is void. The argument list 19, void is not the same as 19, int, therefore, the specialization is not a match and the primary template will be used.

So to make this work, you have to make sure that the type produced by your std::enable_if argument matches the default argument of the last parameter of the primary template, i.e., either change the default argument to be void

template <size_t N, typename = void>
struct T
{
    …
};

or make your std::enable_if produce int in the cases where you want the specialization to be used

template <size_t N>
struct T<N, std::enable_if_t<(N>10), int>>
{
    …
};
Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39