SFINAE 没有正确禁用不明确的重载
SFINAE does not properly disable ambiguous overloads
我正在尝试构建一个更灵活的 std::equal_to
版本,它可以在两种不同类型的对象上调用 T1
和 T2
并且只需要 T1::operator==(T2)
或T2::operator==(T1)
定义。我正在使用 C++17。首先,我有一些结构可以测试是否定义了 SomeType::operator==(OtherType)
:
#include <vector>
#include <utility>
#include <functional>
template<class FromT, class ToT, class Dummy = void>
struct EqualityOperatorDefined : std::false_type {};
template<class FromT, class ToT>
struct EqualityOperatorDefined <FromT, ToT, decltype(std::declval<FromT>() == std::declval<ToT>())>
: std::true_type {};
我认为这行得通 - 至少,在下面用 true
或 false
替换 EqualityOperatorDefined
的用法不会使问题消失。实际灵活的equal_to
是这样的:
template<class T1, class T2>
class FlexibleEqualsTo
{
private:
static constexpr bool cmpT1toT2 = EqualityOperatorDefined<T1, T2>::value;
static constexpr bool cmpT2toT1 = EqualityOperatorDefined<T2, T1>::value;
public:
template<class Dummy = T1>
constexpr bool operator()(const std::enable_if_t<cmpT1toT2, Dummy> & lhs, const T2& rhs)
{
return lhs == rhs;
}
template<class Dummy = T1>
constexpr bool operator()(const std::enable_if_t<cmpT2toT1 && !cmpT1toT2, Dummy> & lhs, const T2& rhs)
{
return rhs == lhs;
}
};
int main() {
FlexibleEqualsTo<int, int> foo;
}
你可以找到完整的代码here at godbolt。
但是,GCC 和 Clang 都抱怨这里的错误重载。 GCC 告诉我:
<source>: In instantiation of 'class FlexibleEqualsTo<int, int>':
<source>:32:32: required from here
<source>:25:17: error: 'template<class Dummy> constexpr bool FlexibleEqualsTo<T1, T2>::operator()(std::enable_if_t<(FlexibleEqualsTo<T1, T2>::cmpT2toT1 && (! FlexibleEqualsTo<T1, T2>::cmpT1toT2)), Dummy>&, const T2&) [with Dummy = Dummy; T1 = int; T2 = int]' cannot be overloaded with 'template<class Dummy> constexpr bool FlexibleEqualsTo<T1, T2>::operator()(std::enable_if_t<FlexibleEqualsTo<T1, T2>::cmpT1toT2, Dummy>&, const T2&) [with Dummy = Dummy; T1 = int; T2 = int]'
25 | constexpr bool operator()(const std::enable_if_t<cmpT2toT1 && !cmpT1toT2, Dummy> & lhs, const T2& rhs)
| ^~~~~~~~
<source>:19:17: note: previous declaration 'template<class Dummy> constexpr bool FlexibleEqualsTo<T1, T2>::operator()(std::enable_if_t<FlexibleEqualsTo<T1, T2>::cmpT1toT2, Dummy>&, const T2&) [with Dummy = Dummy; T1 = int; T2 = int]'
19 | constexpr bool operator()(const std::enable_if_t<cmpT1toT2, Dummy> & lhs, const T2& rhs)
| ^~~~~~~~
std::enable_if
中的两个布尔表达式只能有一个为真。因此,operator()
的另一个(在这种情况下:第二个)定义应该被 SFINAE 去掉,并且不应该有重载问题。
我想我避免了应用 SFINAE 的常见陷阱。两个函数模板都依赖于它们的模板参数。
这里出了什么问题?
operator==
的 SFINAE 检测器有问题。替换为:
decltype(std::declval<FromT>() == std::declval<ToT>())
有了这个:
decltype(std::declval<FromT>() == std::declval<ToT>(), void())
我正在尝试构建一个更灵活的 std::equal_to
版本,它可以在两种不同类型的对象上调用 T1
和 T2
并且只需要 T1::operator==(T2)
或T2::operator==(T1)
定义。我正在使用 C++17。首先,我有一些结构可以测试是否定义了 SomeType::operator==(OtherType)
:
#include <vector>
#include <utility>
#include <functional>
template<class FromT, class ToT, class Dummy = void>
struct EqualityOperatorDefined : std::false_type {};
template<class FromT, class ToT>
struct EqualityOperatorDefined <FromT, ToT, decltype(std::declval<FromT>() == std::declval<ToT>())>
: std::true_type {};
我认为这行得通 - 至少,在下面用 true
或 false
替换 EqualityOperatorDefined
的用法不会使问题消失。实际灵活的equal_to
是这样的:
template<class T1, class T2>
class FlexibleEqualsTo
{
private:
static constexpr bool cmpT1toT2 = EqualityOperatorDefined<T1, T2>::value;
static constexpr bool cmpT2toT1 = EqualityOperatorDefined<T2, T1>::value;
public:
template<class Dummy = T1>
constexpr bool operator()(const std::enable_if_t<cmpT1toT2, Dummy> & lhs, const T2& rhs)
{
return lhs == rhs;
}
template<class Dummy = T1>
constexpr bool operator()(const std::enable_if_t<cmpT2toT1 && !cmpT1toT2, Dummy> & lhs, const T2& rhs)
{
return rhs == lhs;
}
};
int main() {
FlexibleEqualsTo<int, int> foo;
}
你可以找到完整的代码here at godbolt。
但是,GCC 和 Clang 都抱怨这里的错误重载。 GCC 告诉我:
<source>: In instantiation of 'class FlexibleEqualsTo<int, int>':
<source>:32:32: required from here
<source>:25:17: error: 'template<class Dummy> constexpr bool FlexibleEqualsTo<T1, T2>::operator()(std::enable_if_t<(FlexibleEqualsTo<T1, T2>::cmpT2toT1 && (! FlexibleEqualsTo<T1, T2>::cmpT1toT2)), Dummy>&, const T2&) [with Dummy = Dummy; T1 = int; T2 = int]' cannot be overloaded with 'template<class Dummy> constexpr bool FlexibleEqualsTo<T1, T2>::operator()(std::enable_if_t<FlexibleEqualsTo<T1, T2>::cmpT1toT2, Dummy>&, const T2&) [with Dummy = Dummy; T1 = int; T2 = int]'
25 | constexpr bool operator()(const std::enable_if_t<cmpT2toT1 && !cmpT1toT2, Dummy> & lhs, const T2& rhs)
| ^~~~~~~~
<source>:19:17: note: previous declaration 'template<class Dummy> constexpr bool FlexibleEqualsTo<T1, T2>::operator()(std::enable_if_t<FlexibleEqualsTo<T1, T2>::cmpT1toT2, Dummy>&, const T2&) [with Dummy = Dummy; T1 = int; T2 = int]'
19 | constexpr bool operator()(const std::enable_if_t<cmpT1toT2, Dummy> & lhs, const T2& rhs)
| ^~~~~~~~
std::enable_if
中的两个布尔表达式只能有一个为真。因此,operator()
的另一个(在这种情况下:第二个)定义应该被 SFINAE 去掉,并且不应该有重载问题。
我想我避免了应用 SFINAE 的常见陷阱。两个函数模板都依赖于它们的模板参数。
这里出了什么问题?
operator==
的 SFINAE 检测器有问题。替换为:
decltype(std::declval<FromT>() == std::declval<ToT>())
有了这个:
decltype(std::declval<FromT>() == std::declval<ToT>(), void())