'Adding unrelated operator == renderers spaceship operator(<=>) non-functional

In this code example, it seems that adding unreleated operator== (on a different type) renders the shaceship opreator not functional. This is tested in VS2019. Is it a compiler bug, or some weird behaviour?

class A
{
public:
  int x;
};

class B
{
public:
  constexpr std::strong_ordering operator<=>(const B& other) const = default;
  constexpr bool operator==(const A& other) const { return this->x == other.x; } // remove this so it can compile
  int x;
};

class C
{
public:
  constexpr operator bool() const { return b1 == b2; }
  B b1, b2;
};

The error I get is in the usage of the operator in the class B is:

binary '==': no operator found which takes a left-hand operand of type 'const B' (or there is no acceptable conversion)



Solution 1:[1]

Referring to cppreference's page on how defaulted comparison operators work, let's examine what's happening.

For the first case, with no user-declared operator==:

class B
{
public:
  constexpr std::strong_ordering operator<=>(const B& other) const = default;
  int x;
};

The following applies:

If operator<=> is defaulted and operator== is not declared at all, then operator== is implicitly defaulted.

The default signature for operator== is bool operator==(const B& other) const. All well and good.


In your second version, you do declare an operator==. This means you do not get the defaulted operator== as a side-effect of defaulting operator<=>.

class B
{
public:
  constexpr std::strong_ordering operator<=>(const B& other) const = default;
  constexpr bool operator==(const A& other) const { return this->x == other.x; }
  // No additional `operator==` will be generated aside from the above
  int x;
};

The problem is that the operator== you declared takes a const A& as the right hand side, instead of a const B&. So you cannot use this operator== to perform a check of the form b1 == b2.


If you want to have both the operator==(const A&) const overload and the default operator==(const B&) const overload, you can explicitly request that the latter be defaulted, like so:

class B
{
public:
  constexpr std::strong_ordering operator<=>(const B& other) const = default;
  constexpr bool operator==(const B& other) const = default; 
  // Explicitly request default operator== as well as default operator<=>
  constexpr bool operator==(const A& other) const { return this->x == other.x; }

  int x;
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nathan Pierson