1

I'm trying to test at compile time whether two types are equality-comparable, and I've defined operator== for them so they should be. yet, the following code does not compile:

#include <string_view>

struct A { int n; };
bool operator==(const A& a, const std::string_view s) { return a.n == s.size(); }

static_assert(std::equality_comparable_with<A, std::string_view>);

(godbolt) I've even tried defining operator== for the opposite order as well, and for A with itself, but it won't work either.

of course, something like the following works just fine

static_assert(std::equality_comparable_with<std::string, std::string_view>);

what am I missing here?

aradarbel10
  • 435
  • 3
  • 10

1 Answers1

3

Equality means more than just operator== is valid and returns true. And the standard library concepts require this.

equality_comparable defines symmetric comparison (equality comparing between the same type). In terms of syntax, this means that t1==t2 has to be a boolean. But there are also semantic requirements that types are expected to provide.

One could define equality between t1 and t2 as follows. If they are equal, then for any (pure) function f which acts on a T, f(t1) == f(t2).

But for equality between types, this definition is conceptually insufficient. After all, a function f which takes a T may not take a U.

To deal with this fact, C++20 defines equality between T and U in terms of a hypothetical third type C. C could be T, U, or some actual other type. But the main thing C++20 asymmetric equality requires is that there is a type C (which allows symmetric equality testing) to which references to T and U can be implicitly converted. This is the common_reference type.

This allows the standard to define asymmetric equality in terms of C. That is, for any function f which takes a C, if t == u, then f(t) == f(u).

Consider string and string_view. A string is implicitly convertible to a string_view. As such, when doing asymmetric comparisons, the comparison conceptually acts as if any string were converted to a string_view before doing the comparison. That is, string_view acts as the type C.

This provision is here specifically to stop code of the form you're trying to write. Conceptually, your struct A has no equivalence relationship with string_view. A function that takes a string_view couldn't take an A of equal value at all, even if string_view were the common reference between them.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    That definition of equality in terms of pure functions is not very helpful in practice: consider `auto f(const std::vector &v) {return v.capacity();}`. – Davis Herring Dec 05 '21 at 20:10