Search code examples
c++c++11template-specializationenable-if

Why specialization based on enable_if is not picked up by compiler


I'd like to specialize class for some type of classes, for example based on std::is_arithmetic. Although compiler doesn't "see" my specialization based on "enable_if" and chooses principle/main template. Could you please help me with this... Below is snippet of code and output after compiling with g++ 4.8

#include < iostream >  
#include < type_traits >  
#include < string >  

template < typename T1, typename T2 = void >  
struct TestT  
{  
    static const bool is_int = false;  
    static const bool is_str = false;  
};

template < typename T>  
struct TestT < T,  
       std::enable_if< std::is_arithmetic<t>::value, T >::type >  
{  
    static const bool is_int = true;  
    static const bool is_str = false;  
};  

template < typename T>
struct TestT < std::string, T >  
{  
    static const bool is_int = false;  
    static const bool is_str = true;  
};  

class enum TestE  
{  
    Last  
};

int main(int argc, char* argv[])  
{
    std::cout << "Enum is_int: " << TestT<TestE>::is_int  
              << ", is_str: " << TestT<TestE>::is_str << std::endl;  
    std::cout << "string is_int: " << TestT<std::string>::is_int  
              << ", is_str: " << TestT<std::string>::is_str << std::endl;  
    std::cout << "int is_int: " << TestT<int>::is_int  
              << ", is_str: " << TestT<int>::is_str << std::endl;  
    return 0;
}  

Output of above is:

Enum is_int: 0, is_str: 0 // Expected
string is_int: 0, is_str: 1 // Expected
int is_int: 0, is_str: 0 // Not expected

I really would appreciate for any help, and thank you in advance


Solution

  • You need to leave the second parameter (the type aliased by ::type) either unspecified or void so that it matches the primary template's default argument:

    struct TestT<T,  
           std::enable_if<std::is_arithmetic<T>::value>::type> 
    

    You also need typename before the std::enable_if statement, or use std::enable_if_t (and leave out ::type):

    struct TestT<T, std::enable_if_t<std::is_arithmetic<T>::value>>
    

    Same goes for the second specialization:

    template<>
    struct TestT<std::string>  
    {  
        static const bool is_int = false;  
        static const bool is_str = true;  
    };
    

    And lastly, within this specialization, is_int should be set to true:

    template<typename T>  
    struct TestT<T, std::enable_if_t<std::is_arithmetic<T>::value>>  
    {  
        static const bool is_int = true;  
        static const bool is_str = false;  
    };
    

    Live Demo

    A better version might be to keep a single specialization and use std::is_same to test for int and a type trait to test for strings:

    template<class T>struct is_string:std::false_type{};
    template<>struct is_string<std::string>:std::true_type{};
    template<std::size_t N>struct is_string<char const(&)[N]>:std::true_type{};
    template<>struct is_string<char const*>:std::true_type{};
    template<>struct is_string<char const*const>:std::true_type{};
    template<>struct is_string<char const*volatile>:std::true_type{};
    // on and on...
    
    template<typename T>  
    struct TestT  
    {  
        static const bool is_int = std::is_same<T, int>();  
        static const bool is_str = is_string<T>();  
    };