在测试 SFINAE 时,我发现了一些我认为不应该起作用的东西
While testing SFINAE, I found something that I don't think should work
我从 this site 中发现了一个有趣的条件函数排除,在测试它时我遇到了这个:
#include<type_traits>
namespace detail
{
enum enabler {};
}
template <int overload, typename Condition>
using EnableIf = std::enable_if_t<Condition::value, detail::enabler>;
template <typename T,
EnableIf<0, std::is_same<T, int>>...>
T twice(T t) { return 2 * t; }
template <typename T,
EnableIf<0, std::is_same<T, float>>...>
T twice(T t) { return 2 * t; }
int main()
{
twice(1);
twice(1.f);
return 0;
}
这不会导致编译器错误,因为两个函数中的 EnableIf
类型相同吗?我打算为每个重载使用不同的数字,并有一个包含启用程序枚举的模板 class,以便它成为不同的类型,但似乎没有必要。这是缺陷还是我遗漏了什么?
我已经在 VC++ (2017) 和 clang.
上对此进行了测试
尽管 VC++ 编译器没有报错,但智能感知显然不喜欢它:
我是这么想的,但无论我使用不同的整数,还是不厌其烦地按照我说的去做,它都会抱怨。
Shouldn't this cause a compiler error because the EnableIf
type are the same in both functions?
但它们并不相同。一个拿一包std::enable_if_t<std::is_same<T, int>::value, detail::enabler>
,一个拿一包std::enable_if_t<std::is_same<T, float>::value, detail::enabler>
。这些是完全不相关的类型,因此您声明了两个不相关的函数模板。实际上,这两种类型中至少有一种是病式的,因此它们不能具有相同的类型——充其量都是病式的。
这很好。不同的数字不仅是不必要的,而且不会做任何事情 - 别名模板是透明的,所以数字会消失,因为您没有在实际类型表达式中使用它。
只有当两个 EnableIf
条件 都 可行时,然后 你最终会得到相同的类型(detail::enabler
) 在两个函数中。
看来我抽得太快了,将 Barry's answer 标记为正确答案。不过好像不太对,至少不清楚。
一个函数可以有一个有效的重载,只要它的参数不同。模板函数是一样的,但它的签名还包括模板参数。因此,你可以有相同的函数名和相同的参数列表,但模板参数不同,它仍然会被认为是重载。
因此,如果两个函数在同一类型下都可行 T
,那么就会发生冲突。
但是,由于有一个 EnableIf
只会在互斥条件下解析为一个类型,因此即使第二个模板类型会解析为相同的类型 detail::enabler
,第一个模板parameter 将是不同的类型(即使没有基于函数参数的自动推导,具有相同的函数参数类型,这仍然有效)。因此,这仍然是一个不同的函数重载。
对于@Barry 所说的 std::enable_if_t<std::is_same<T, int>::value, detail::enabler>
和 std::enable_if_t<std::is_same<T, float>::value, detail::enabler>
是完全不相关的类型。他是正确的。当有效时,它们确实 完全 相同的类型,但他没有提到的是它们永远不会同时有效。
我从 this site 中发现了一个有趣的条件函数排除,在测试它时我遇到了这个:
#include<type_traits>
namespace detail
{
enum enabler {};
}
template <int overload, typename Condition>
using EnableIf = std::enable_if_t<Condition::value, detail::enabler>;
template <typename T,
EnableIf<0, std::is_same<T, int>>...>
T twice(T t) { return 2 * t; }
template <typename T,
EnableIf<0, std::is_same<T, float>>...>
T twice(T t) { return 2 * t; }
int main()
{
twice(1);
twice(1.f);
return 0;
}
这不会导致编译器错误,因为两个函数中的 EnableIf
类型相同吗?我打算为每个重载使用不同的数字,并有一个包含启用程序枚举的模板 class,以便它成为不同的类型,但似乎没有必要。这是缺陷还是我遗漏了什么?
我已经在 VC++ (2017) 和 clang.
上对此进行了测试尽管 VC++ 编译器没有报错,但智能感知显然不喜欢它:
我是这么想的,但无论我使用不同的整数,还是不厌其烦地按照我说的去做,它都会抱怨。
Shouldn't this cause a compiler error because the
EnableIf
type are the same in both functions?
但它们并不相同。一个拿一包std::enable_if_t<std::is_same<T, int>::value, detail::enabler>
,一个拿一包std::enable_if_t<std::is_same<T, float>::value, detail::enabler>
。这些是完全不相关的类型,因此您声明了两个不相关的函数模板。实际上,这两种类型中至少有一种是病式的,因此它们不能具有相同的类型——充其量都是病式的。
这很好。不同的数字不仅是不必要的,而且不会做任何事情 - 别名模板是透明的,所以数字会消失,因为您没有在实际类型表达式中使用它。
只有当两个 EnableIf
条件 都 可行时,然后 你最终会得到相同的类型(detail::enabler
) 在两个函数中。
看来我抽得太快了,将 Barry's answer 标记为正确答案。不过好像不太对,至少不清楚。
一个函数可以有一个有效的重载,只要它的参数不同。模板函数是一样的,但它的签名还包括模板参数。因此,你可以有相同的函数名和相同的参数列表,但模板参数不同,它仍然会被认为是重载。
因此,如果两个函数在同一类型下都可行 T
,那么就会发生冲突。
但是,由于有一个 EnableIf
只会在互斥条件下解析为一个类型,因此即使第二个模板类型会解析为相同的类型 detail::enabler
,第一个模板parameter 将是不同的类型(即使没有基于函数参数的自动推导,具有相同的函数参数类型,这仍然有效)。因此,这仍然是一个不同的函数重载。
对于@Barry 所说的 std::enable_if_t<std::is_same<T, int>::value, detail::enabler>
和 std::enable_if_t<std::is_same<T, float>::value, detail::enabler>
是完全不相关的类型。他是正确的。当有效时,它们确实 完全 相同的类型,但他没有提到的是它们永远不会同时有效。