c++concept

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


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


Solution

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