Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How c++ compiler matches "Class::operator=="?

I found the following code can't be compiled before C++20:

#include <iostream>
using namespace std;

struct B;
struct C;
struct A {
  A(int v) : val(v) {}
  bool operator==(A &a) { return val == a.val; }

private:
  int val;
  friend struct B;
};
struct B : private A {
  B(int v) : A(v) {};
  bool operator==(A &a) { return this->val == a.val; }
  bool operator==(B &a) { return this->val == a.val; }
};

int main() {
  A a(1);
  B b(2);
  cout << (a == a) << endl;
  cout << (a == b) << endl;
  cout << (b == a) << endl;
  cout << (b == b) << endl;
}

My g++ version:

g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0

Compile in C++17

g++ main.cpp -std=c++17
main.cpp: In function ‘int main()’:
main.cpp:24:17: error: ‘A’ is an inaccessible base of ‘B’
   24 |   cout << (a == b) << endl;
      |                 ^

Compile in c++20:

g++ main.cpp -std=c++20
./a.out
1
0
0
1

It seems that after C++20, if compiler finds a == b is invalid, it will try to replace a == b with b == a instead.

Is my guess correct?

like image 954
AlphaGolang Avatar asked Sep 20 '25 04:09

AlphaGolang


1 Answers

It seems that after c++20, if compiler finds a == b is invalid, it will try to replace a == b with b == a instead. Was my guess correct?

Almost. A synthesized candidate is added to the candidate set of overloads where the order of the two parameters is reversed, so that part is correct:

[over.match.oper]/3.4.4

For the equality operators, the rewritten candidates also include a synthesized candidate, with the order of the two parameters reversed, for each non-rewritten candidate for the expression y == x.

But that means that even if a == b would be valid (using A::operator==), it'd still use B::operator== since that requires fewer conversions. It's the same if you'd have an overloaded function like this:

void foo(const A&); // would be used by `a`
void foo(const B&); // would be used by `b`

So, with the inheritance made public ...

struct B;

struct A {
    A(int v) : val(v) {}
    bool operator==(const A&) const noexcept;

   private:
    int val;
    friend struct B;
};

struct B : public A {     // made public
    B(int v) : A(v) {}

    bool operator==(const A&) const noexcept;
    bool operator==(const B&) const noexcept;
};

... different overloads will be called in C++17 and C++20:

cout << (a == b) << endl; // C++17: A::operator==(const A&)
                          // C++20: B::operator==(const A&)

Demo

like image 109
Ted Lyngmo Avatar answered Sep 22 '25 17:09

Ted Lyngmo