Should Operator≪≪ Be Implemented as a Friend or as a Member Function

Should operator be implemented as a friend or as a member function?

The problem here is in your interpretation of the article you link.

Equality

This article is about somebody that is having problems correctly defining the bool relationship operators.

The operator:

  • Equality == and !=
  • Relationship < > <= >=

These operators should return a bool as they are comparing two objects of the same type. It is usually easiest to define these operators as part of the class. This is because a class is automatically a friend of itself so objects of type Paragraph can examine each other (even each others private members).

There is an argument for making these free standing functions as this lets auto conversion convert both sides if they are not the same type, while member functions only allow the rhs to be auto converted. I find this a paper man argument as you don't really want auto conversion happening in the first place (usually). But if this is something you want (I don't recommend it) then making the comparators free standing can be advantageous.

Streaming

The stream operators:

  • operator << output
  • operator >> input

When you use these as stream operators (rather than binary shift) the first parameter is a stream. Since you do not have access to the stream object (its not yours to modify) these can not be member operators they have to be external to the class. Thus they must either be friends of the class or have access to a public method that will do the streaming for you.

It is also traditional for these objects to return a reference to a stream object so you can chain stream operations together.

#include <iostream>

class Paragraph
{
public:
explicit Paragraph(std::string const& init)
:m_para(init)
{}

std::string const& to_str() const
{
return m_para;
}

bool operator==(Paragraph const& rhs) const
{
return m_para == rhs.m_para;
}
bool operator!=(Paragraph const& rhs) const
{
// Define != operator in terms of the == operator
return !(this->operator==(rhs));
}
bool operator<(Paragraph const& rhs) const
{
return m_para < rhs.m_para;
}
private:
friend std::ostream & operator<<(std::ostream &os, const Paragraph& p);
std::string m_para;
};

std::ostream & operator<<(std::ostream &os, const Paragraph& p)
{
return os << p.to_str();
}


int main()
{
Paragraph p("Plop");
Paragraph q(p);

std::cout << p << std::endl << (p == q) << std::endl;
}

Why is it recommended to declare functions as friends in operator overloading

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

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; }

What operators should be declared as friends?

This really depends on whether a class is going to be on the left- or right-hand side of the call to operator== (or other operator). If a class is going to be on the right-hand side of the expression—and does not provide an implicit conversion to a type that can be compared with the left-hand side—you need to implement operator== as a separate function or as a friend of the class. If the operator needs to access private class data, it must be declared as a friend.

For example,

class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
};

allows you to compare a message to a string

Message message("Test");
std::string msg("Test");
if (message == msg) {
// do stuff...
}

but not the other way around

    if (msg == message) { // this won't compile

You need to declare a friend operator== inside the class

class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
friend bool operator==(const std::string& lhs, const Message& rhs);
};

or declare an implicit conversion operator to the appropriate type

class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
operator std::string() const;
};

or declare a separate function, which doesn't need to be a friend if it doesn't access private class data

bool operator==(const std::string& lhs, const Message& rhs);

friend in operator == or when should i use it?

In the first case, your function operator== is a nonstatic class member. It has therefore access to private and protected member variables.

In the second case, the operator is externally declared, therefore it should be defined as a friend of the class to access those member variables.

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

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

friend keyword for operator overloading

You want to stream objects whose internals are not accessible through their class' public interface, so the operator can't get at them. Then you have two choices: Either put a public member into the class which does the streaming

class T {
public:
void stream_to(std::ostream& os) const {os << obj.data_;}
private:
int data_;
};

and call that from the operator:

inline std::ostream& operator<<(std::ostream& os, const T& obj)
{
obj.stream_to(os);
return os;
}

or make the operator a friend

class T {
public:
friend std::ostream& operator<<(std::ostream&, const T&);
private:
int data_;
};

so that it can access the class' private parts:

inline std::ostream& operator<<(std::ostream& os, const T& obj)
{
os << obj.data_;
return os;
}


Related Topics



Leave a reply



Submit