Skip to content

CWG2673 [over.match.oper] Spaceship operator doesn't sufficiently suppress builtin-operators #205

@brevzin

Description

@brevzin

Reflector thread: https://lists.isocpp.org/core/2019/10/7407.php

Reference (section label): [over.match.oper]

Issue description:

Consider this example:

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

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. The wording in [over.match.oper]/3.3 currently says (emphasis mine):

For all other operators, the built-in candidates include all of the candidate operator functions defined in [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.

When we're evaluating E::Lo > E::Hi, the user-provided operator<=> isn't a non-member candidate, it's a rewritten candidate, so gcc continues to include the built-in. Patrick Palka makes the same argument in a gcc bug opened for this here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200

The same thing happens for operator== vs operator!=. A user-declared operator== will be called for e1 == e2 but not for e1 != e2, the latter is specified to call the built-in per the wording (although clang and msvc don't implement it that way).

Suggested resolution:

This seems like an oversight in the adoption of <=> and the new rewrite rules for ==, where the wording here did not change. Change /3.3.4 to read:

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

clang and msvc already appear to implement it this way.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions