Overload operators as member function or non-member (friend) function?
Asked Answered
T

6

12

I am currently creating a utility class that will have overloaded operators in it. What are the pros and cons of either making them member or non-member (friend) functions? Or does it matter at all? Maybe there is a best practice for this?

Tahoe answered 15/12, 2009 at 6:0 Comment(0)
A
19

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).

Adman answered 15/12, 2009 at 6:16 Comment(1)
Now this has shed a lot of light on the vicious circle " Too many/few operators " that buzzed me. Thank you! Also, as a beginner programmer, I would say the observation related to automatic type conversions on both sides is a very good one and probably useful in the future.Leund
W
23

I'd go with "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices": if you can do it as non-member function, do it as non-member function (in the same namespace).

One of the reasons: it works better with implicit type conversion. An Example: You have a complex class with an overloaded operator*. If you want to write 2.0 * aComplexNumber, you need the operator* to be a non-member function.

Another reason: less coupling. Non-member-functions a less closely coupled than member functions. This is almost always a good thing.

Warder answered 8/1, 2010 at 7:31 Comment(0)
A
19

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).

Adman answered 15/12, 2009 at 6:16 Comment(1)
Now this has shed a lot of light on the vicious circle " Too many/few operators " that buzzed me. Thank you! Also, as a beginner programmer, I would say the observation related to automatic type conversions on both sides is a very good one and probably useful in the future.Leund
Q
6

If you plan on implementing streaming operators (<< and >>) then they will be non-members methods because your object is on the left of the operator.

If you plan on implementing ->, () or [] they are naturally member methods.

For the others (comparison and mathematical) you should check out Boost.Operators, it really helps.

For example, if you want to implement the following operators:

MyClass& MyClass::operator+=(int);
MyClass operator+(const MyClass&, int);
MyClass operator+(int, const MyClass&);

You only have to write:

class MyClass: boost::operator::addable<MyClass,int> // no need for public there
{
public:
  MyClass& operator+=(int);
private:
};

The 2 operator+ will be automatically generated as non-members which will let you benefit from automatic conversions. And they will be implemented efficiently in term of operator+= so you write code only once.

Quinn answered 15/12, 2009 at 10:23 Comment(0)
W
3

If you are implementing op, then most probably you need to implement op=. i.e. if you are overloading + operator, then you should implement +=. Make sure that you are returning const to an object if you are doing post-increment or overloading + operator. So, if you overload operator + , then implement it as a non-member operator and use += operator inside it. For eg.

const A operator+(const A& lhs, const A& rhs)
{
   A ret(lhs);
   ret += rhs;
   return ret;
}
Welterweight answered 15/12, 2009 at 6:41 Comment(3)
What is the point of returning const A ? I have seen this a few times (returning a const value) and could never found any practical reason...Quinn
@Matthieu M., it prevents us from writing "(a+b)=c", which is already natively impossible with primitive types like int.Spearwort
@Jagannath, You forgot the "const" for both parameter declarations.Spearwort
H
3

For binary operators, one limitation of member functions is that the left object must be of your class type. This can limit using the operator symmetrically.

Consider a simple string class:

class str
{
public:
    str(const char *);
    str(const str &other);
};

If you implement operator+ as a member function, while str("1") + "2" will compile, "1" + str("2") will not compile.

But if you implement operator+ as a non-member function, then both of those statements will be legal.

Hemoglobin answered 15/12, 2009 at 6:56 Comment(0)
V
1

There is nothing like best practices but it depends on the operator you are overloading ..

For e.g .

  1. >> and << can't be overloaded as member functions .

  2. Suppose you want to do like this : obj1 = 2 * obj2 then go for non-member function.

For binary operator overloading member function takes only 1 parameter (invoking object is impcliitly passed ) whereas non-member function takes 2 parameters .

Vagina answered 15/12, 2009 at 6:15 Comment(2)
cout and cin are instances of classes in iostream, they cannot be overloaded, OP is talking about overloading operators.Spic
<< and >> certainly can be overloaded as member functions, just not if you want to use them as the stream extraction/insertion operators. If you want to use them as shift operators, like Dennis Ritchie intended, no problem.Finale

© 2022 - 2024 — McMap. All rights reserved.