0

I have read this at many places which recommend of using "friend" while overload operators but no body explained it clearly as to why it is really needed? Why can not we declare them as plain member function? Any shortcoming?

Googled this but did not get any clear answer.

Hemant Bhargava
  • 2,431
  • 1
  • 17
  • 33

7 Answers7

4

Sometimes you can't declare operator overload as a member function, like in IO operator<< and operator>>. The first parameter of these functions has to be ostream or istream, which are library classes and you can't extend them, declaring such functions as friend gives them access to private variables of your class.

w.b
  • 10,250
  • 5
  • 25
  • 44
2

Using friend means it's a nonmember friend function.

For improving encapsulation by minimizing dependencies, it's better to declare nonmember nonfriend functions. If it need to access the private/protected member of the class, make it friend. At last, make it a member function.

Here's an algorithm to determine whether a function should be a member and/or friend, from [C++ Coding Standards: 101 Rules, Guidelines, and Best Practices By Herb Sutter, Andrei Alexandrescu](Item 44. Prefer writing nonmember nonfriend functions):

// If you have no choice then you have no choice; make it a member if it must be:

If the function is one of the operators =, ->, [], or (), which must be members:

    Make it a member.

// If it can be a nonmember nonfriend, or benefits from being a nonmember friend, do it:

Else if: a) the function needs a different type as its left-hand argument (as do operators >> or <<, for example); or b) it needs type conversions on its leftmost argument; or c) it can be implemented using the class's public interface alone:

    Make it a nonmember (and friend if needed in cases a) and b) ).

    If it needs to behave virtually:

        Add a virtual member function to provide the virtual behavior, and implement the nonmember in terms of that.

Else: Make it a member.

In some case, such as above-mentioned a) and b), you can't achieve them by member function, you have to declare them as nonmember function, and make them friend if need to access the private/protected member of the class.

songyuanyao
  • 147,421
  • 15
  • 261
  • 354
1

There are a few reasons people use friend:

  • sometimes granting friendship is actually reasonable, as the public API shouldn't expose some members than need to be compared

  • it's convenient for a lazy programmer to grant access to all the private and protected data members, ensuring you can write the operator implementation without needing to go back to grant access later or use a less obvious/direct public function (that's NOT a good reason, just a lazy one)

  • you can define the operator function inside the class, where any template parameters, typedefs, constants etc. don't need to be explicitly qualified as they would in the surrounding [namespace] scope. That's considerably simpler for those new to C++.

e.g.:

    template <typename T>
    struct X
    {
        friend bool operator==(const X& lhs, const X& rhs) { ... }
    };

...vs... ...struct X as above without ==...

    template <typename T>
    bool operator==(const X<T>& lhs, const X<T>& rhs) { ... }
  • in a two-birds-with-one-stone scoop, it makes the function nominally inline, avoiding One Definition Rule complications

Only the first reason above is a compelling functional reason for making the operator a friend, rather than making it a non-member function, given the lesser encapsulation and correspondingly higher maintenance burden involved.

There are excellent reasons though to prefer either a friend or non-friend non-member function to a member function, as an implicit constructor can then kick in to allow the operator to work with one instance of the class and another value from which a second instance can be constructed:

struct X { X(int); };
bool operator==(const X& lhs, const X& rhs);

x == 3;   // ok for member or non-member operator==
3 == x;   // only works for non-member operator== after implicit X(3) for lhs
Tony Delroy
  • 94,554
  • 11
  • 158
  • 229
0

You are listing two options only for overloaded operator, while actually there are three:

  • global function, not friend
  • member function
  • global function, friend of the class

You did not list the first one, and yet it is the recommended one. If you can define an operator in terms of existing class public interface, define it as global function outside the class. In this way you do not extend the class public interface without need, minimizing the number of functions with access to the class private members.

What to do if the operator needs to access the class private members? Then you have two options - member function or a friend global function. From those two member function is preferable because it is cleaner. However, in some cases it is not possible to define an overloaded operator as the member function. If the object of your class is right-hand argument to two-argument operator, then global function is the only choice.

Wojtek Surowka
  • 18,864
  • 4
  • 43
  • 48
0

I assume that we are comparing global-scope friends that are defined in class and non-friends.
The reasons that the former is sometimes preferred are that such functions...

  • ... need access to the data members for the operation to be performed. This can also be done by loosening encapsulation by providing the data through public getters, but that's not always desired.

  • ... shall only be found via ADL to avoid pollution of some namespaces and overload candidate sets. (Only friend functions that are defined in the class they are a friend of satisfy this!)

Moreover, a minor goodie is that for class templates, it's easier to define a global function that operates on specializations inside them since we avoid making the function a template. Also the function is implicitly inline. All this shortens the code, but is no primary reason.

Columbo
  • 57,033
  • 7
  • 145
  • 194
0

If your implementation followed proper data encapsulation then you might not exposed your data variables to outside world and all data members will be declared as private.

But using operator overloading most of the times you will be accessing the data members , please note it here you will be accessing the data members outside your class. So to provide access to the data members outside your class it is advised to declare the operator overloaded function as friend.

But this might not be required for unary operators , as it will operate on the data members of the particular class in which it is called.

Let me know if you need any examples for your understanding.

evk1206
  • 331
  • 5
  • 16
0

There are serious advantages to non-member functions when dealing with operators.

Most operators are binary (take two arguments) and somewhat symmetrical, and with a member operator they only work if *this is the left-hand side argument. So you need to use a non-member operator.

The friend pattern both gives the operator full access to the class (and, operators are usually intimate enough that this isn't harmful), and it makes it invisible outside of ADL. In addition, there are significant advantages if it is a template class.

Invisible outside of ADL lets you do crazy stuff like this:

struct bob {
  template<class Lhs, class Rhs>
  friend bob operator+( Lhs&&, Rhs&& ) { /* implementation */ }
}

here our operator+ is a template that seemingly matches anything. Except because it can only be found via ADL on bob, it will only be matched if it is used on at least one bob object. This technique can let you pick up on rvalue/lvalue overloads, do SFINAE testing on properties of types, etc.

Another advantage with template types is that the operator ends up not being a template function. look here:

template<class T>
struct X {};

template<class T>
bool operator==( X<T>, X<T> ) { return true; }

template<class T>
struct Y {
  friend bool operator==( Y, Y ) { return true; }
};

struct A {
  template<class T>
  operator X<T>() const { return {}; }
};

struct B {
  template<class T>
  operator Y<T>() const { return {}; }
};


int main() {
  A a;
  X<int> x;
  B b;
  Y<int> y;
  b == y; // <-- works!
  a == x; // <-- fails to compile!
}

X<T> has a template operator==, while Y<T> has a friend operator==. The template version must pattern-match both arguments to be a X<T>, or it fails. So when I pass in an X<T> and a type convertible to X<T>, it fails to compile, as pattern matching does not do user defined conversions.

On the other hand, Y<T>'s operator== is not a template function. So when b == y is called, it is found (via ADL on y), then b is tested to see if it can convert to a y (it can be), and the call succeeds.

template operators with pattern matching are fragile. You can see this problem in the standard library in a few points where an operator is overloaded in ways that prevent conversion from working. Had the operator been declared a friend operator instead of a public free template operator, this problem can be avoided.

Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465