为什么 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
#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