Operator overloading : member function vs. non-member function?
If you define your operator overloaded function as member function, then the compiler translates expressions like s1 + s2
into s1.operator+(s2)
. That means, the operator overloaded member function gets invoked on the first operand. That is how member functions work!
But what if the first operand is not a class? There's a major problem if we want to overload an operator where the first operand is not a class type, rather say double
. So you cannot write like this 10.0 + s2
. However, you can write operator overloaded member function for expressions like s1 + 10.0
.
To solve this ordering problem, we define operator overloaded function as friend
IF it needs to access private
members. Make it friend
ONLY when it needs to access private members. Otherwise simply make it non-friend non-member function to improve encapsulation!
class Sample
{
public:
Sample operator + (const Sample& op2); //works with s1 + s2
Sample operator + (double op2); //works with s1 + 10.0
//Make it `friend` only when it needs to access private members.
//Otherwise simply make it **non-friend non-member** function.
friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}
Read these :
A slight problem of ordering in operands
How Non-Member Functions Improve Encapsulation
Operator overload: Member vs. non-member when only same type objects can be involved
Because operator==
has symmetric semantics for its LHS and RHS argument, the recommended approach is to always implement it as a non-member in terms of the public interface of its operands (or if private data is required, to declare it as a friend inside the class).
So
class Bla
{
public:
// complete interface to data required for comparison
auto first();
auto second();
// ... more
private:
// data goes here
};
bool operator==(Bla const& L, Bla const& R)
{
return
std::forward_as_tuple(L.first(), L.second() /*, ... */) ==
std::forward_as_tuple(R.first(), R.second() /*, ... */)
;
}
This way, implicit conversion to Bla
are considered for both the L and R arguments (I'm not saying implicit conversions are a good idea, but if you have those, it's better to avoid surprises where they are only considered for the RHS argument).
friend vs member functions in Operator Overloading C++
The choice isn't "member or friend" but "member or non-member".
(Friendship is frequently overused, and usually taught much too early in schools.)
This is because you can always add a public member function that a free function can call.
For instance:
class A
{
public:
explicit A(int y) : x(y) {}
A plus(const A& y) const { return A{x + y.x}; }
private:
int x;
};
A operator+(const A& lhs, const A& rhs) { return lhs.plus(rhs); }
As for how to choose: if the operator doesn't take an instance of the class as its left-hand operand, it must be a free function, otherwise it's pretty much a matter of personal taste (or coding standards if you're not alone).
Example:
// Can't be a member because the int is on the left.
A operator+ (int x, const A& a) { return A{x} + a; }
For operators that have a corresponding mutating operator (like +
and +=
), it's common to do the mutating operator as a member and the other as a non-member:
class B
{
public:
explicit B(int y) : x(y) {}
B& operator+= (const B& y) { x += y.x; return *this; }
private:
int x;
};
B operator+(B lhs, const B& rhs) { return lhs += rhs; }
but you can spell this out too, of course:
class C
{
public:
explicit C(int y) : x(y) {}
C& add(const C& y) { x += y.x; return *this; }
private:
int x;
};
C& operator+=(C& lhs, const C& rhs) { return lhs.add(rhs); }
C operator+(C lhs, const C& rhs) { return lhs += rhs; }
Why are relational operators overloaded as non-member function in STL string? [duplicate]
Overloading them as non-member functions allows the LHS of the operator to be converted to type std::string
. For example, the following does not work should operator==
be a member:
std::string name = "foo";
if ("foo" == name)
// ...
That's because "foo".operator==(name)
is not a valid expression.
Why is function overloading between member functions and non-member functions not allowed?
You're mostly asking why so rather than saying "because the language says so", lets show an example where your suggestion causes everything to fail.
Suppose I have a handy class in my github repo:
struct HandyClass {
void display(short c) {
std::cout << c;
}
void doStuff() {
display(3);
}
};
And then some other developer in a different github repro makes a handy set of display functions:
void display(double v) {
showWindowsPopup("Your score was %f", v);
}
void display(int v) {
showWindowsPopup("Your score was %d", v);
}
You download both repros, and suddenly, HandyClass doesn't work right anymore:
#include "displays.h"
#include "handyclass.h"
int main() {
HandyClass a;
a.doStuff(); //Why does this show a windows popup!?!?
}
Because you included the display headers first, then display(3)
matched to ::display(int)
instead of ::HandyClass::display(short)
, because 3
is an int
. And much sadness occurs.
But with the official C++ lookup rules, this doesn't happen. Since my class has display
functions, it will ignore functions outside the class, to prevent mistakes, so that HandyClass
always does the same thing for everyone.
Overload operators as member function or non-member (friend) function?
Each operator has its own considerations. For example, the << operator (when used for stream output, not bit shifting) gets an ostream as its first parameter, so it can't be a member of your class. If you're implementing the addition operator, you'll probably want to benefit from automatic type conversions on both sides, therefore you'll go with a non-member as well, etc...
As for allowing specialization through inheritance, a common pattern is to implement a non-member operator in terms of a virtual member function (e.g. operator<< calls a virtual function print() on the object being passed).
Is there any reason to not overload operator== as member, when only comparing to other object of the class?
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.
operator overloading and non-member functions c++
Here is the addition operator outside of the class:
Complex operator+(const Complex& lhs, const Complex& rhs) {
//implement the math to add the two
return Complex(lhs.aGetValue() + rhs.aGetValue(),
lhs.bGetValue() + rhs.bGetValue());
}
Of course you will need to declare aGetValue()
and bGetValue()
as const
:
double aGetValue() const {return a;}
double bGetValue() const {return b;}
Member function vs Operator overloading
If an operator maps directly to the meaning of the function, then it's recommended to use the operator, e.g.
==
instead ofequals
because it compares equality,+
instead ofadd
if it's adding e.g. mathematical vectors,- but not
+
instead ofadd
if it's e.g. appending to a list.
Related Topics
When Should I Write the Keyword 'Inline' For a Function/Method
How to Get the List of Files in a Directory Using C or C++
Why Is Unsigned Integer Overflow Defined Behavior But Signed Integer Overflow Isn'T
What Xml Parser Should I Use in C++
What Is the Curiously Recurring Template Pattern (Crtp)
Single Quotes Vs. Double Quotes in C or C++
C++ Returning Reference to Local Variable
What Are the Main Purposes of Using Std::Forward and Which Problems It Solves
Meaning of 'Const' Last in a Function Declaration of a Class
What Are the Barriers to Understanding Pointers and What Can Be Done to Overcome Them
How to Convert a Std::String to Const Char* or Char*
How to Reverse a String in Place in C or C++
In Which Scenario Do I Use a Particular Stl Container
Print a Binary Tree in a Pretty Way
Why Can't C++ Be Parsed With a Lr(1) Parser
Difference Between Public, Private, and Protected Inheritance in C++