Compiler Explorer link
template <typename T>
concept HasEq = requires(T t) {
{ t == t } -> std::convertible_to<bool>;
};
struct X {};
static_assert(not HasEq<X>);
//bool a = pair<X, X>{} == pair<X, X>{};
static_assert(! HasEq<pair<X, X>>); // fails! SIGH
I suppose it’s simple enough to define a concept for 'T has support for =='. And it’s simple enough to define a type 'X' which doesn't support the operator==. And the concept seems to work fine for that.
But it is confusing that pair<X,X> doesn't really support operator== (since it delegates to the X operator== that doesn’t exist).
And yet HasEq<pair<X, X>> returns the wrong answer (it says operator== is defined).
This appears to be a bug with the std C++ definitions of operator==(pair,pair), defining operator== unconditionally, instead of using 'enable_if' or 'requires' on the operator== definition. But I'm not really sure what I can do about that to make HasEq work properly (so it would start with understanding if this is really a defect in the std::pair operator== definition).
OK, I may have found an answer (thanks to hints in comments above!), but it makes me feel I need to bathe.
https://godbolt.org/z/3crzGdvP5
#include <concepts>
#include <utility>
using namespace std;
namespace PRIVATE_ {
template <typename T>
concept HasEqBasic = requires(T t) {
{ t == t } -> std::convertible_to<bool>;
};
template <typename T>
constexpr inline bool has_eq_v = HasEqBasic<T>;
template <typename T, typename U>
constexpr inline bool has_eq_v<std::pair<T, U>> = has_eq_v<T> and has_eq_v<U>;
template <typename... Ts>
constexpr inline bool has_eq_v<std::tuple<Ts...>> = (has_eq_v<Ts> and ...);
} // namespace PRIVATE_
template <typename T>
concept HasEq = PRIVATE_::has_eq_v<T>;
struct X {};
static_assert(not HasEq<X>);
static_assert(!HasEq<pair<X, X>>);
After some tinkering, I came up with this. Wiser heads will tell me what might be wrong with it:
template <typename T>
concept BasicHasEq = requires(T t) {
{ t == t } -> std::convertible_to <bool>;
};
template <typename T>
concept IsPair = requires (T t) {
t.first;
t.second;
};
template <typename T>
concept IsNonComparablePair = IsPair <T> &&
(!BasicHasEq <decltype (std::declval <T> ().first)> ||
!BasicHasEq <decltype (std::declval <T> ().second)>);
template <typename T>
concept IsContainer = requires (T t) {
typename T::value_type;
};
template <typename T>
concept IsNonCopyableContainer = IsContainer <T> && !BasicHasEq <typename T::value_type>;
template <typename T>
concept HasEq = BasicHasEq <T> && !IsNonComparablePair <T> && !IsNonCopyableContainer <T>;
Live demo
Edited to also handle container types. More work probably still needed to handle all eventualities.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With