如何修复运算符==existing的SFINAE检查,使其与std::pair一起工作



我有一个简单的SFINAE检查器,它检查给定类型(T(是否定义了operator==

这通常工作得相当好,但对std::pair<>不起作用。

我想我理解为什么它不起作用——std::pair<>似乎无条件地提供operator==支持,即使底层类型是一对不支持。

我正在就如何更正我的SFINAEoperator==检查器征求意见,以便它与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;
}

要修复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>
{};

演示

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

我们还可以编写一个助手,其第一个目的是提高代码的表达能力:

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

进一步阅读:

  • 你应该读一下检测习语,这是我的is_detected_v的一个更灵活的实现
  • C++20概念使我们的实现方式更加简单。我认为这些概念是我们在标准库中没有std::is_detected的主要原因。只是概念不需要它

这是我最简单的解决方案:向#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;

最新更新