如何修复 SFINAE 检查 operator== 是否存在,以便它与 std::pair 一起使用
how to fix SFINAE check for operator== existing, so that it works with std::pair
我有一个简单的 SFINAE 检查器,用于检查给定类型 (T
) 是否定义了 operator==
。
这通常效果很好,但不适用于 std::pair<>
。
我想我明白为什么它不起作用 - std::pair<>
似乎无条件地提供 operator==
支持,即使它的一对不支持。
我正在就如何更正我的 SFINAE operator==
检查器征求意见,以便它与 pair<>
一起使用
您可以在 https://onlinegdb.com/rmAQS7V091
在线测试此代码
但为了完整起见,它也直接包含在这里:
#include <iostream>
#include <functional> // needed for std::equal_to
#include <iterator>
using namespace std;
namespace
{
using namespace std;
/**
* LIFTED -@todo add docs/reference
* from Stroustrup C++11 book - page 800
*/
struct substitution_failure
{
};
/**
* LIFTED -@todo add docs/reference
* from Stroustrup C++11 book - page 800
*/
template < typename T > struct substitution_succeeded:true_type
{
};
template <> struct substitution_succeeded <substitution_failure >:false_type
{
};
#define STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS(NAME, XTEST) \
namespace Private_ { \
template <typename T> \
struct NAME##_result_impl { \
template <typename X> \
static auto check (const X& x) -> decltype (XTEST); \
static substitution_failure check (...); \
using type = decltype (check (declval<T> ())); \
}; \
} \
template <typename T> \
using NAME##_result = typename Private_::NAME##_result_impl<T>::type; \
template <typename T> \
struct has_##NAME : integral_constant<bool, not is_same<NAME##_result<T>, substitution_failure>::value> { \
}; \
template <typename ITERABLE> \
constexpr bool Has##NAME##_v = has_##NAME<ITERABLE>::value;
STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS (eq, (x == x)); // SEE https://stroika.atlassian.net/browse/STK-749
STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS (equal_to, (static_cast<bool> (std::equal_to<X>{}(x, x))));
class SimpleClassWithoutComparisonOperators
{
public:
SimpleClassWithoutComparisonOperators (size_t v);
SimpleClassWithoutComparisonOperators (const
SimpleClassWithoutComparisonOperators
& f);
~SimpleClassWithoutComparisonOperators ();
size_t GetValue () const;
static size_t GetTotalLiveCount ();
//explicit operator size_t () const { return fValue; }
SimpleClassWithoutComparisonOperators operator+ (const
SimpleClassWithoutComparisonOperators
& rhs) const
{
return SimpleClassWithoutComparisonOperators (fValue + rhs.fValue);
}
private:
size_t fValue;
int fConstructed;
static size_t sTotalLiveObjects;
};
static_assert (!has_eq < SimpleClassWithoutComparisonOperators >::value);
using PAIR_ =
std::pair < SimpleClassWithoutComparisonOperators,
SimpleClassWithoutComparisonOperators >;
static_assert (!has_eq < PAIR_ >::value); /// THIS IS WHAT FAILS
}
int
main ()
{
cout << "Hello World";
return 0;
}
这是我天真的修复:在 #define
、
中添加两行代码
template <typename X> \
static substitution_failure check (std::pair<X,X>); \
一共:
#define STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS(NAME, XTEST) \
namespace Private_ { \
template <typename T> \
struct NAME##_result_impl { \
template <typename X> \
static auto check (const X& x) -> decltype (XTEST); \
static substitution_failure check (...); \
template <typename X> \
static substitution_failure check (std::pair<X,X>); \
using type = decltype (check (declval<T> ())); \
}; \
} \
template <typename T> \
using NAME##_result = typename Private_::NAME##_result_impl<T>::type; \
template <typename T> \
struct has_##NAME : integral_constant<bool, not is_same<NAME##_result<T>, substitution_failure>::value> { \
}; \
template <typename ITERABLE> \
constexpr bool Has##NAME##_v = has_##NAME<ITERABLE>::value;
正如 kenash0625 正确指出的那样,您需要 std::pair
的专门化来手动实施 std::pair
本身不执行的检查。但是,在这一点上,我建议使用 C++17 且不使用宏来简化您的实现:
template<
template<typename...> typename Detector,
typename T,
typename SFINAE = void>
constexpr inline bool is_detected_v = false;
template<
template<typename...> typename Detector,
typename T>
constexpr inline bool is_detected_v<
Detector, T, std::void_t<Detector<T>>> = true;
我们现在可以像这样实现 has_eq
:
template<typename T>
using has_eq_t = decltype(std::declval<T>() == std::declval<T>());
static_assert(is_detected_v<has_eq_t, std::pair<int, int>>);
我们也可以写一个helper,其首要目的是增加代码的表现力:
template<typename T>
constexpr inline bool has_eq_v = is_detected_v<has_eq_t, T>;
static_assert( ! has_eq_v<SimpleClassWithoutComparisonOperators>);
虽然最初的问题还没有解决,但这个助手的第二个目的是我们可以为 std::pair
执行一些临时检查。让我们添加专业:
template<typename T, typename U>
constexpr inline bool has_eq_v<std::pair<T, U>> = has_eq_v<T> && has_eq_v<U>;
在这里我们可以为此类特定情况添加越来越多的专业化:
template<typename... Ts>
constexpr inline bool has_eq_v<std::tuple<Ts...>> = (has_eq_v<Ts> && ...);
static_assert ( ! has_eq_v<PAIR_>);
进一步阅读:
- 您应该了解 Detection Idioms, this is a more flexible implementation of my
is_detected_v
- C++20 概念使我们的实现方式更加简单。我认为这些概念是我们在标准库中没有
std::is_detected
的主要原因。只是概念不需要它。
要修复 std::pair
案例,您可以针对它专门化您的特征:
template <typename T1, typename T2>
struct has_eq<std::pair<T1, T2>> :
integral_constant<bool, has_eq<T1>::value && has_eq<T1>::value>
{};
我有一个简单的 SFINAE 检查器,用于检查给定类型 (T
) 是否定义了 operator==
。
这通常效果很好,但不适用于 std::pair<>
。
我想我明白为什么它不起作用 - std::pair<>
似乎无条件地提供 operator==
支持,即使它的一对不支持。
我正在就如何更正我的 SFINAE operator==
检查器征求意见,以便它与 pair<>
您可以在 https://onlinegdb.com/rmAQS7V091
在线测试此代码但为了完整起见,它也直接包含在这里:
#include <iostream>
#include <functional> // needed for std::equal_to
#include <iterator>
using namespace std;
namespace
{
using namespace std;
/**
* LIFTED -@todo add docs/reference
* from Stroustrup C++11 book - page 800
*/
struct substitution_failure
{
};
/**
* LIFTED -@todo add docs/reference
* from Stroustrup C++11 book - page 800
*/
template < typename T > struct substitution_succeeded:true_type
{
};
template <> struct substitution_succeeded <substitution_failure >:false_type
{
};
#define STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS(NAME, XTEST) \
namespace Private_ { \
template <typename T> \
struct NAME##_result_impl { \
template <typename X> \
static auto check (const X& x) -> decltype (XTEST); \
static substitution_failure check (...); \
using type = decltype (check (declval<T> ())); \
}; \
} \
template <typename T> \
using NAME##_result = typename Private_::NAME##_result_impl<T>::type; \
template <typename T> \
struct has_##NAME : integral_constant<bool, not is_same<NAME##_result<T>, substitution_failure>::value> { \
}; \
template <typename ITERABLE> \
constexpr bool Has##NAME##_v = has_##NAME<ITERABLE>::value;
STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS (eq, (x == x)); // SEE https://stroika.atlassian.net/browse/STK-749
STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS (equal_to, (static_cast<bool> (std::equal_to<X>{}(x, x))));
class SimpleClassWithoutComparisonOperators
{
public:
SimpleClassWithoutComparisonOperators (size_t v);
SimpleClassWithoutComparisonOperators (const
SimpleClassWithoutComparisonOperators
& f);
~SimpleClassWithoutComparisonOperators ();
size_t GetValue () const;
static size_t GetTotalLiveCount ();
//explicit operator size_t () const { return fValue; }
SimpleClassWithoutComparisonOperators operator+ (const
SimpleClassWithoutComparisonOperators
& rhs) const
{
return SimpleClassWithoutComparisonOperators (fValue + rhs.fValue);
}
private:
size_t fValue;
int fConstructed;
static size_t sTotalLiveObjects;
};
static_assert (!has_eq < SimpleClassWithoutComparisonOperators >::value);
using PAIR_ =
std::pair < SimpleClassWithoutComparisonOperators,
SimpleClassWithoutComparisonOperators >;
static_assert (!has_eq < PAIR_ >::value); /// THIS IS WHAT FAILS
}
int
main ()
{
cout << "Hello World";
return 0;
}
这是我天真的修复:在 #define
、
template <typename X> \
static substitution_failure check (std::pair<X,X>); \
一共:
#define STROIKA_FOUNDATION_CONFIGURATION_DEFINE_HAS(NAME, XTEST) \
namespace Private_ { \
template <typename T> \
struct NAME##_result_impl { \
template <typename X> \
static auto check (const X& x) -> decltype (XTEST); \
static substitution_failure check (...); \
template <typename X> \
static substitution_failure check (std::pair<X,X>); \
using type = decltype (check (declval<T> ())); \
}; \
} \
template <typename T> \
using NAME##_result = typename Private_::NAME##_result_impl<T>::type; \
template <typename T> \
struct has_##NAME : integral_constant<bool, not is_same<NAME##_result<T>, substitution_failure>::value> { \
}; \
template <typename ITERABLE> \
constexpr bool Has##NAME##_v = has_##NAME<ITERABLE>::value;
正如 kenash0625 正确指出的那样,您需要 std::pair
的专门化来手动实施 std::pair
本身不执行的检查。但是,在这一点上,我建议使用 C++17 且不使用宏来简化您的实现:
template<
template<typename...> typename Detector,
typename T,
typename SFINAE = void>
constexpr inline bool is_detected_v = false;
template<
template<typename...> typename Detector,
typename T>
constexpr inline bool is_detected_v<
Detector, T, std::void_t<Detector<T>>> = true;
我们现在可以像这样实现 has_eq
:
template<typename T>
using has_eq_t = decltype(std::declval<T>() == std::declval<T>());
static_assert(is_detected_v<has_eq_t, std::pair<int, int>>);
我们也可以写一个helper,其首要目的是增加代码的表现力:
template<typename T>
constexpr inline bool has_eq_v = is_detected_v<has_eq_t, T>;
static_assert( ! has_eq_v<SimpleClassWithoutComparisonOperators>);
虽然最初的问题还没有解决,但这个助手的第二个目的是我们可以为 std::pair
执行一些临时检查。让我们添加专业:
template<typename T, typename U>
constexpr inline bool has_eq_v<std::pair<T, U>> = has_eq_v<T> && has_eq_v<U>;
在这里我们可以为此类特定情况添加越来越多的专业化:
template<typename... Ts>
constexpr inline bool has_eq_v<std::tuple<Ts...>> = (has_eq_v<Ts> && ...);
static_assert ( ! has_eq_v<PAIR_>);
进一步阅读:
- 您应该了解 Detection Idioms, this is a more flexible implementation of my
is_detected_v
- C++20 概念使我们的实现方式更加简单。我认为这些概念是我们在标准库中没有
std::is_detected
的主要原因。只是概念不需要它。
要修复 std::pair
案例,您可以针对它专门化您的特征:
template <typename T1, typename T2>
struct has_eq<std::pair<T1, T2>> :
integral_constant<bool, has_eq<T1>::value && has_eq<T1>::value>
{};