Search code examples
c++c++11templatessfinaetemplate-templates

Why does SFINAE not work with std::enable_if_t?


#include <vector>
#include <list>
#include <iostream>

template
<
    typename T,
    template<typename, typename = std::allocator<T>> class C,
    typename = std::enable_if_t
    <
    std::is_same<std::vector<T>, C<T>>::value
    >
>
void f(const C<T>& coll)
{
    std::cout << "vector" << std::endl;
}

template
<
    typename T,
    template<typename, typename = std::allocator<T>> class C,
    typename = std::enable_if_t
    <
    std::is_same<std::list<T>, C<T>>::value
    >
>
void f(const C<T>& coll)
{
    std::cout << "list" << std::endl;
}

int main()
{
        std::vector<int> c1{ 1, 2, 3 };
        std::list<int> c2{ 1, 2, 3 };       
        f(c1);
        f(c2);
}

Clang 3.8 complains:

error : template parameter redefines default argument: typename = std::enable_if_t

What's wrong in the code?


Solution

  • Why you need SFINAE? A simple overload is enough!

    template <typename T>
    void f(const std::vector<T>& coll)
    {
        std::cout << "vector" << std::endl;
    }
    
    template < typename T>
    void f(const std::list<T>& coll)
    {
        std::cout << "list" << std::endl;
    }
    
    int main()
    {
        std::vector<int> c1{ 1, 2, 3 };
        std::list<int> c2{ 1, 2, 3 };
        f(c1);
        f(c2);
    }
    

    And if you really want to use SFINAE ( with no sense in that case ) you can fix your definition with:

    template
    <   
    typename T,
             template<typename, typename = std::allocator<T>> class C,
             typename std::enable_if_t
             <
             std::is_same<std::list<T>, C<T>>::value
             >* = nullptr
        >
    void f(const C<T>& coll);
    

    And why your definition did not work can be found already here: SFINAE working in return type but not as template parameter