Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any reason to not overload operator== as member, when only comparing to other object of the class?

I've been combing through the internet to find an answer, but I couldn't find any. The only reasons given seems to be relevant for comparing with objects of different type (e.g. MyClass == int). But the most common use case is comparing a class instance to another instance of the same class, not to any unrelated type.

In other words, I do understand the problems with:

struct A {
    bool operator==(int b);
};

But I cannot find any good reason to not use member function in the most obvious use-case:

struct A {
    bool operator==(const A&);
};

  • The most canonical duplicate What are the basic rules and idioms for operator overloading? says "overload binary operators as non-member" as rule of a thumb.
  • Operator overloading : member function vs. non-member function? gives example mentioned above - if you were to use this operator with instance of another class/primitive type...
  • CppCoreGuidelines has a vague explanation "If you use member functions, you need two", which I assume applies to comparing with object of different type.
  • Why should operator< be non-member function? mentions that "non-member functions play better with implicit conversion", but it seems again the case of left-hand operand not being the instance of the class.

On the other hand, member overload seems to have a couple positive sides:

  • No need to befriend the function or to provide getters for members
  • It is always available to class users (although this might be the downside also)
  • No problems with lookup (which seems to be common in our GoogleTests for some reason)

Is overloading operator== as non-member function just a convention to keep it the same with possible overloads in other classes? Or are there any other reasons to make it non-member?

like image 933
Yksisarvinen Avatar asked Sep 13 '19 16:09

Yksisarvinen


People also ask

Can we overload == operator?

Overloading Binary Operators Suppose that we wish to overload the binary operator == to compare two Point objects. We could do it as a member function or non-member function. To overload as a member function, the declaration is as follows: class Point { public: bool operator==(const Point & rhs) const; // p1.

Can I overload operator == so it lets me compare two char [] using a string comparison?

[13.6] Can I overload operator== so it lets me compare two char[] using a string comparison? No: at least one operand of any overloaded operator must be of some class type.

Can we overload == operator in C++?

You can redefine or overload the function of most built-in operators in C++. These operators can be overloaded globally or on a class-by-class basis. Overloaded operators are implemented as functions and can be member functions or global functions.

Can comparison operators be overloaded?

You can overload any of these operators, which can be used to compare the objects of a class. Following example explains how a < operator can be overloaded and similar way you can overload other relational operators.


2 Answers

Well, in your question, you did forget to const qualify the member function, and it would be harder to write bool operator==(A&, const A&); by accident.

If you had an implicit constructor, a class with implicit conversion to A or base class with an operator== with higher priority, the member function wouldn't work if it was on the left, but would if it was on the right. Although most of the time implicit conversions are a bad idea, inheritance could reasonably lead to a problem.

struct A {
    A(int);  // Implicit constructor
    A();

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

struct B : A {
    bool operator==(const B&) const;
};

void test() {
    A a;
    B b;
    // 1 == a;  // Doesn't work
    a == 1;
    // b == a;  // Doesn't work; Picks `B::operator==(const B&) const;`
    a == b;  // Picks `A::operator==(const A&) const`, converting `b` to an `A&`.
    // Equality is no longer symmetric as expected
}

In the future, with the C++20 operator<=>, you will most likely always implement this as a member function (namely as auto operator<=>(const T&) const = default;), so we know that this guideline may change.

like image 157
Artyer Avatar answered Nov 10 '22 10:11

Artyer


The arguments for using non-member operator overload for symmetric operations are based on style and consistency. They are not very strong arguments 1. Non-member overloads are typically preferred because a weak argument is still a little bit better than no argument at all.

Your arguments for member operator overload don't seem to be any stronger. Consider following:

No need to befriend the function

On the other hand, if you use a non-member overload, then you don't have need to declare a member function. Is befriending the non-member somehow worse?

or to provide getters for members

There is no need for that if you befriend the overload.

It is always available to class users (although this might be the downside also)

It is unclear how this differs from the non-member overloads. Are they also not always available to the class users?

No problems with lookup (which seems to be common in our GoogleTests for some reason)

Are there lookup problems with non-member overloads? Could you demonstrate the problem with an example, and show how the problem is solved by using a member overload instead?

If it does solve the problem, then you can of course use that. Just because some guidelines recommend that you prefer one alternative as a rule of thumb, doesn't mean that is the only alternative to be used in all use cases.


1 Although, see answer https://stackoverflow.com/a/57927564/2079303 which is arguably stronger than just stylistic.

like image 6
eerorika Avatar answered Nov 10 '22 12:11

eerorika