为什么 SFINAE 不适用于 std::enable_if_t?

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 抱怨:

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

代码有什么问题?

默认模板参数不参与函数匹配。
从编译器的角度来看,当它试图寻找合适的函数时,它们都具有以下签名:

template <typename T, template<typename, typename> class C, typename>
void f(const C<T>& coll);

因此,f的第二个定义实际上是重新定义。

考虑一个最小的例子:

template<typename = void>
void f() {}

template<typename = char>
void f() {}

// ...

f<>();

编译器如何确定 f 你想调用什么?
您的代码中或多或少会发生同样的情况。

enable_if_t 移动到 return 类型,它将按预期工作。
在这种情况下,由于错误(正如 sfinae 表达式所预期的那样),替换将排除两个定义之一。

为什么需要SFINAE?一个简单的重载就足够了!

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);
}

如果您真的想使用 SFINAE(在那种情况下没有任何意义),您可以通过以下方式修正您的定义:

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);

为什么你的定义不起作用可以在这里找到: SFINAE working in return type but not as template parameter