简体   繁体   中英

Three-way comparison operator member vs non-member implementation

Two-way comparison operators should be non-members functions if:

  • you want the first operand to be of a type that is not this class
  • you want implicit type conversion any of the two operands

The new C++20 three-way comparison operator has symmetric generation rules. The name lookup for an expression a@b , where @ is a two-way comparison operator, is done in the order a@b , a<=>b and b<=>a (with this order of preference in case of ambiguity when selecting the best match from the overload resolution set). See P0515R2 for details. This means the operator <=> can be a member function and still allow the first operand to not be of this class type.

The paper, however, contains this note:

Normally, operator<=> should be just a member function; you will still get conversions on each parameter because of the symmetric generation rules in §2.3. In the rare case that you also want to support conversions on both parameters at the same time (to enabling compare two objects neither of which is of this type, but using this type's comparison function), make it a non-member friend.

If I understand this correctly it says non-member implementation should be only needed if implicit conversion on both operands at the same time is needed? Is that correct? Can I see an actual example when that is needed? I am thinking of this although it does not seem like a valid example:

struct foo
{
   foo(int const x) 
      : data{ x } {}
   foo(std::string_view x) 
      : data{std::stoi(x.data())}{}

   friend auto operator<=>(foo const & lhv, foo const & rhv) noexcept
   {
      return lhv.data <=> rhv.data;
   }

private:
   int data;
};


int main()
{
   assert(foo {42} == foo {"42"});        // OK
   assert(42 == std::string_view("42"));  // ??
}

Here's an illustrative (though not necessarily practical) example:

struct A {
    int i;
};

struct B {
    B(A a) : i(a.i) { }

    int i;
};

strong_ordering operator<=>(B const& lhs, B const& rhs) {
    return lhs.i <=> rhs.i;
}

A{2} == A{2}; // okay, true
A{2} < A{1};  // okay, false

We find the candidate taking two B s at global scope, and it's viable, so we convert both arguments and use it. If that operator was either a member function or a non-member friend declared within the class, name lookup wouldn't have found it.


Note that in the OP, <=> is declared as a non-member friend within the class. This means that name lookup would not find it for 42 == string_view("42") , since neither of those arguments is a foo . You would need to add a normal non-member declaration to make it visible for such lookup.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM