我有一个简单的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;