Specializing class with SFINAE

2019-06-24 09:21发布

问题:

I want to write a template class which checks a trait with SFINAE.

As classes can not be "overloaded" as I read in that post: template overloading and SFINAE working only with functions but not classes

I wrote the following code:

class AA { public: using TRAIT = int; };
class BB { public: using TRAIT = float; };

template < typename T, typename UNUSED = void> class X;

template < typename T>
class X<T, typename std::enable_if< std::is_same< int, typename T::TRAIT>::value, int >::type>
{
    public:
        X() { std::cout << "First" << std::endl; }
 };

template < typename T>
class X<T, typename std::enable_if< !std::is_same< int, typename T::TRAIT>::value, unsigned int >::type>
{
    public:
        X() { std::cout << "Second" << std::endl; }
};

int main()
{
    X<AA> a;
    X<BB> b;
}

But it simply fails with:

error: aggregate 'X<AA> a' has incomplete type and cannot be defined
         X<AA> a;
               ^

error: aggregate 'X<BB> b' has incomplete type and cannot be defined
         X<BB> b;

It seams that none of the templates works but I get no hint from the compiler why both specializations fail.

回答1:

The specializations have to match the primary. The lookup rule for determining what X<AA> is is to first match the primary and add the default types, which gets us to X<AA, void>, and then try to match that against all the specializations. But none of your specializations match X<AA, void> so you end up with the primary. The primary isn't a complete type, hence the error.

Why do none of them match? Because you wrote:

typename std::enable_if< std::is_same< int, typename T::TRAIT>::value, int >::type

For AA that evalutes as int, which doesn't match void, so the specialization isn't considered. You just want:

typename std::enable_if< std::is_same< int, typename T::TRAIT>::value>::type

or really:

std::enable_if_t< std::is_same< int, typename T::TRAIT>::value>

Similarly, for BB, the second specialization's second type evaluates as unsigned int instead of void - so it too doesn't match.



标签: c++ c++14 sfinae