Adam
Adam

Reputation: 1817

error: no match for ‘operator==’, but indeed defined on base class

struct BaseA {
    auto operator==(const BaseA& other) const {return other.a == a;}
    int a;
};

struct B {
    int b;
};

struct A: public BaseA {
    auto operator==(const B& other) const {return other.b == a;}
};

int main() {
    A a{10};
    a == a;

    return 0;
}

It won't compile:

error: no match for ‘operator==’ (operand types are ‘A’ and ‘A’)
note: candidate: ‘auto A::operator==(const B&) const’
note: no known conversion for argument 1 from ‘A’ to ‘const B&’

Doesn't list BaseA::operator== as a candidate.

However, if I comment out A::operator== method, it compiles.

Because of that I thought comparison operators get some special treatment, (sometimes generated for the child class, sometimes not, like those out of rule-of-five), but after a quick search turns out not the case.

Some rules of operator overloading, then?

Upvotes: 1

Views: 148

Answers (3)

Adam
Adam

Reputation: 1817

I found a fix, without writing out using for each comparison operator I add in the child class:

Make the operator non-member and pull it out to the enclosing scope of A (in this example the global scope):

auto operator==(const A&, const B&)

If you want to access private members of A or the protected of its base class, you can make the operator a friend of A. (Same for B.)

Why it works

The hiding (meaning that if the name is found in a scope, it won't look in next ones, those the lookup algorithm is going to consider in the next step) won't stop this from working because for operators, two separate lookups, subsequently unioned, are performed:

For an operator used in expression (e.g., operator+ used in a+b) two separate lookups are performed: for the non-member operator overloads and for the member operator overloads (for the operators where both forms are permitted). Those sets are then merged with the built-in operator overloads on equal grounds.

-- cppreference

Thus the non-member operator== in global namespace won't prevent searching for BaseA::operator==, which is searched for in the member operator lookup, starting in A (but there's no operator== now), so moving on the the next scope, the base class BaseA.


This way of fixing it is important for me, because I use a strong types library and I just wanted to add just one operator for the strong type, wrapping an integer. I'm using the library ti avoid writing out the boilerplate myself, and yet every time I would add an operator, I would have to use the one in the library.

Upvotes: 0

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123094

Nothing special about operators here, you'd get the similar error with:

struct BaseA {
    auto foo(const BaseA& other) const {return other.a == a;}
    int a;
};

struct B {
    int b;
};

struct A: public BaseA {
    auto foo(const B& other) const {return other.b == a;}
};

int main() {
    A a{10};
    a.foo(a);
}

Compiler finds a foo in A and stops there. If you want to have both you need to explicitly pull it in scope:

struct A: public BaseA {
    auto foo(const B& other) const {return other.b == a;}
    using BaseA::foo; 
};

Upvotes: 2

Jarod42
Jarod42

Reputation: 218098

auto operator==(const B& other) const hides the base one, use using

struct A: public BaseA {
    using BaseA::operator==;
    auto operator==(const B& other) const {return other.b == a;}
};

Demo

Upvotes: 4

Related Questions