user53670
user53670

Reputation:

operator overloading in c++

If I want to overload operator +, which prototype is correct?

  1. D operator+(const D& lhs, const D& rhs);
    then declare it as a friend function of D.

  2. D operator+(const D& s);
    Then declare it as a member function of D.

Upvotes: 5

Views: 417

Answers (10)

Alexandre C.
Alexandre C.

Reputation: 56986

The first one has a different behavior if D has implicit constructors.

Consider

struct D
{
    D(int);
};

D operator+(const D&, const D&);

Then you can do 1 + d and d + 1, which you cannot do with the member operator+ (the lhs must be a D, and no conversion shall take place).

Upvotes: 0

Frigo
Frigo

Reputation: 1723

Both are correct (after fixing the const correctness problem others pointed out), but I recommend the member operator. It can be overridden with polymorphic behavior if virtual, have better cohesion with the class and as a result it is easier to maintain and to document. Try to avoid friend functions if you can.

As for the "derived = base + derived" case, I recommend not mixing value semantics operators and polymorphism, it can have unforeseen consequences due to various implicit conversion sequences and object slicing. That example might be equivalent to derived = Derived(base) + derived, but it can be derived = Derived(base + Base(derived)) as well if base has operator +.

Use only explicit conversion and casting, and you will not encounter any mysterious strange behavior. And think twice before you implement operators for a polymorphic class.

Upvotes: 0

James Kanze
James Kanze

Reputation: 154027

The principle of least surprise says that your overload should behave more or less like the built-in operators. The usual solution here is not to implement operator+ at all, but to implement:

D& operator+=( D const& rhs );

as a member, and then derive from something like:

template<typename T>
class ArithmeticOperators
{
    friend T operator+( T const& lhs, T const& rhs )
    {
        T result( lhs );
        result += rhs;
        return result;
    }
    //  Same thing for all of the other binary operators...
};

This way, you don't have to rewrite the same thing every time you define a class which overloads the arithmetic operators, and you're guaranteed that the semantics of + and += correspond.

(The friend in the above is simply to allow you to put the function, along with its implementation, in the class itself, where ADL will find it.)

Upvotes: 0

I would advice you to follow a third path: Implement operator+= as a member function, and then implement operator+ in terms of the previous like:

D operator+=( D lhs, D const & rhs ) {
   lhs += rhs;
   return lhs;
}

The advantage of the third way is that with basically the same code you provide both + and +=, and you get to implement operator+ as a free function which is an advantage from the point of view of symmetry, if your class has implicit conversions, it will allow d + t and t + d for any object d of type D and any other object t of type implicitly convertible to D. The member function version will only apply conversions to the right hand side, which means that d + t will be allowed, but not t + d.

[self publicity warning] You can read a longer explanation on this particular issue here

Upvotes: 2

john
john

Reputation: 88017

The first is correct, the second is plain wrong. You can improve the second by writing this

D operator+(const D& s) const;

but it's still wrong. The reason is that the compiler will apply different rules to the left and right hand sides of your + operator in the second version. For instance given this code

class C
{
};

class D
{
public:
  D(const C&);
};

C c;
D d;

d = d + c; // legal with both versions
d = c + d; // not legal with the second version

The difference is because the compiler will create a temporary D object from a C object for a method or function argument but it won't do it to make a method call on the temporary object.

In short the first version treats the left hand side and right hand side equally and so agrees better with the coders expectations.

Upvotes: 3

Juraj Blaho
Juraj Blaho

Reputation: 13471

Both methods are almost correct. It's just two ways of doing almost the same. But when you need to apply the binary operator to other types other than D (for example int+D) you need to use the second one.

The option 1 does not even have to be a friend if it does not need access to the private members.

The option 2 have to be fixed a little. You are missing D::.

D D::operator+(const D& s) const {
}

Upvotes: 0

UncleBens
UncleBens

Reputation: 41351

The second one should be

D operator+(const D& s) const;

Then either is good.

As to the first needing to be a friend: only if it really needs access to anything private. Normally you can implement it in terms of the public interface, commonly with the corresponding operator+=:

D operator+(D lhs, const D& rhs) {  //copy left-hand side
    return lhs += rhs;  //add the right-hand side and return by value
}

Upvotes: 2

You only need to declare a function as a friend of a class if you plan to access private variables of the class through this function. In the first prototype you do not need to declare the function as a friend since you are passing in all the values you plan to use. In the second prototype you can declare the function a friend since you will need to access one more variable, but it is better practice and easier to just declare the function as a member.

Upvotes: 0

Kevin
Kevin

Reputation: 25269

I think both are correct. But one thing you missed (that may or may not apply), is that if the left side value can be something other than D (say an integer or something) then option 1 works for that e.g.

D operator+(int lhs, const D& rhs);

Then you can do something like:

D d1;

D d2 = 5 + d1;

Upvotes: 1

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361722

Go with the first one. However, if it needs to access private members,only then make it friend, otherwise make it non-friend function.

Upvotes: 1

Related Questions