129

I read that an overloaded operator declared as member function is asymmetric because it can have only one parameter and the other parameter passed automatically is the this pointer. So no standard exists to compare them. On the other hand, overloaded operator declared as a friend is symmetric because we pass two arguments of the same type and hence, they can be compared.

My question is that when i can still compare a pointer's lvalue to a reference, why are friends preferred? (using an asymmetric version gives the same results as symmetric) Why do STL algorithms use only symmetric versions?

火乔治
  • 19,950
  • 3
  • 44
  • 60
badmaash
  • 4,357
  • 7
  • 40
  • 60
  • 11
    Your question is really only about binary operators. Not all overloaded operators are restricted to a single parameter. The () operator can take any number of parameters. Unary operators, on the other hand, can't have any parameters. – Charles Salvia Jan 07 '11 at 04:16
  • 3
    http://stackoverflow.com/a/4421729/103167 – Ben Voigt Feb 19 '12 at 01:09
  • 4
    This is one of many topics covered in the [C++ FAQ: Operator overloading](http://stackoverflow.com/questions/4421706/operator-overloading) – Ben Voigt Feb 19 '12 at 01:09

2 Answers2

162

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

gedamial
  • 1,466
  • 1
  • 14
  • 27
Nawaz
  • 327,095
  • 105
  • 629
  • 812
  • 3
    "Make it `friend` only when it needs to access private members..and when you dont have/are bored of writing accessors, right? – badmaash Jan 07 '11 at 05:15
  • 4
    @Abhi : Choose your pick : Improved Encapsulation vs Lazy writing habit! – Nawaz Jan 07 '11 at 05:17
  • Are there known cases where you couldn't just avoid the `friend` keyword by having the global `operator+()` simply be `return op2.operator+(op1);`? In both cases, + would by default not modify the input Sample, and instead return a new rvalue instance of `Sample`, right? – matthias Jan 30 '12 at 19:13
  • 7
    @matthias, not all operators are commutative. Simple example is `a/b`. – edA-qa mort-ora-y Apr 25 '12 at 07:31
  • 1
    Wouldn't `Sample&` in `friend Sample& operator + (double op1, const Sample& op2);` return a local reference that is no longer valid on return ? double return values (with double CTOR on Sample) on the friend or adding operator double() const member to Sample seem like good alternatives. You could also return by value on the friend operator. – Hans Roggeman Apr 05 '14 at 18:03
  • 3
    A common way to avoid making your non-member operators require `friend` is to implement them in terms of the operation-assignment operators (which will almost certainly be public members). For example, you could define `T T::operator+=(const T &rhs)` as a member and then define non-member `T operator(T lhs, const T &rhs)` as `return lhs += rhs;`. The non-member function should be defined in the same namespace as the class. – Adrian McCarthy Aug 10 '16 at 18:58
  • @AdrianMcCarthy `operator +=` normally changes `lhs`, but `operator +` normally not! – ricky Oct 30 '18 at 11:27
  • 2
    @ricky: But if the lhs is a copy (as it is in my comment), then the fact that the lhs changes doesn't matter. – Adrian McCarthy Nov 19 '18 at 21:57
  • FWIW, nowadays, some people (myself included) recommend making the operator `friend` even if you don't need to access private members. Otherwise, observe the error message when you write `std::cout << something` without an `operator< – Justin Sep 21 '19 at 01:30
21

It's not necessarily a distinction between friend operator overloads and member function operator overloads as it is between global operator overloads and member function operator overloads.

One reason to prefer a global operator overload is if you want to allow expressions where the class type appears on the right hand side of a binary operator. For example:

Foo f = 100;
int x = 10;
cout << x + f;

This only works if there is a global operator overload for

Foo operator + (int x, const Foo& f);

Note that the global operator overload doesn't necessarily need to be a friend function. This is only necessary if it needs access to private members of Foo, but that is not always the case.

Regardless, if Foo only had a member function operator overload, like:

class Foo
{
  ...
  Foo operator + (int x);
  ...
};

...then we would only be able to have expressions where a Foo instance appears on the left of the plus operator.

Charles Salvia
  • 48,775
  • 12
  • 118
  • 138
  • 3
    +1 for making the distinction between member functions and non-member functions rather than member and friend functions. I guess today we'd say "global or namespace scope." – Adrian McCarthy Aug 10 '16 at 18:46