Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do simple C++ concept has_eq - that works with std::pair (is std::pair operator== broken for C++20)

Tags:

c++

concept

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).

like image 521
lewis Avatar asked Dec 04 '25 14:12

lewis


2 Answers

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>>);
like image 195
lewis Avatar answered Dec 07 '25 03:12

lewis


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.

like image 35
Paul Sanders Avatar answered Dec 07 '25 04:12

Paul Sanders



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!