Title
User-declared spaceship vs. built-in operators
Status
review
Section
12.2.2.3 [over.match.oper]
Submitter
Barry Revzin

Created on 2022-12-30.00:00:00 last changed 3 weeks ago

Messages

Date: 2023-01-07.14:29:44

Proposed resolution (approved by CWG 2023-01-06):

Change 12.2.2.3 [over.match.oper] bullet 3.3.4 as follows:

  • ...
  • do not have the same parameter-type-list as any non-member or rewritten candidate that is not a function template specialization.
Date: 2022-12-30.00:00:00

Consider:

  #include <compare>

  enum class E : int {
    Lo = 0,
    Hi = 1
  };

  constexpr auto operator<=>(E lhs, E rhs) -> std::strong_ordering {
    return (int)rhs <=> (int)lhs;
  }

  // everybody agrees this is true
  static_assert((E::Lo <=> E::Hi) == std::strong_ordering::greater);

  // gcc rejects this, msvc and clang accept
  static_assert(E::Lo > E::Hi);  // #1

The intent here is for the user-provided operator<=> to suppress the built-in operator<=> for E. And gcc, clang, and msvc all agree that this does happen when the comparison expression explicitly uses a <=> b.

But when the comparison expression is a @ b for one of the relational operators, gcc disagrees, conforming to 12.2.2.3 [over.match.oper] bullet 3.3:

For all other operators, the built-in candidates include all of the candidate operator functions defined in 12.5 [over.built] that, compared to the given operator, ... do not have the same parameter-type-list as any non-member candidate that is not a function template specialization.

The issue is that, for #1, the user-provided operator<=> is not a non-member candidate, but a rewritten candidate. A similar situation arises for a user-declared operator==, which will be called for e1 == e2, but not for e1 != e2. Again, clang and MSVC disagree.

History
Date User Action Args
2023-01-07 14:29:44adminsetstatus: open -> review
2023-01-01 19:58:02adminsetmessages: + msg7103
2022-12-30 00:00:00admincreate