Overload operators as member function or non-member (friend) function?
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?
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.
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).
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.
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.