4

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.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • possible duplicate of [Operator overloading](http://stackoverflow.com/questions/4421706/operator-overloading) – AProgrammer Aug 10 '11 at 15:52
  • 1
    Both a valid but I guess you mean which is best practice? I would say probably declaring as a member since you're not opening up the innards of `class D` through a `friend` declaration. – Dan Aug 10 '11 at 15:52
  • Both will work fine. You should have a look at the Operator Overloading FAQ [here](http://stackoverflow.com/questions/4421706/operator-overloading) – Tony The Lion Aug 10 '11 at 15:52
  • I always prefer 2nd one. – iammilind Aug 10 '11 at 15:55
  • It's useful to make the return return type `const D` so that you cannot say `(x + y) = 5;`. – Kerrek SB Aug 10 '11 at 16:00
  • 1
    @Kerrek: I'm not sure if people should be doing that any more as it can interfere with move semantics. –  Aug 10 '11 at 16:01
  • @Mike: Interesting point. How would you solve that? – Kerrek SB Aug 10 '11 at 16:03
  • @Kerrek: Maybe you could return some non-const wrapper object that hides the = operator? Haven't thought about it much. –  Aug 10 '11 at 16:05
  • const-by-value rarely made sense. Any programmer who makes an assignment to an unnamed temporary deserves what they get. – AJG85 Aug 10 '11 at 16:29
  • 4
    @Dan: member operators are not usually the best idea. You should prefer free standing functions (they are symmetric with respect to the types of the arguments). The free function is part of the *interface* and as such it should not be too problematic to declare it as `friend`, but then again, that can also be avoided easily. Implement `operator+=` as a member function, and `operator+` in terms of the previous one: `D operator+( D const & lhs, D const & rhs ) { D copy(lhs); return copy+=rhs; }` – David Rodríguez - dribeas Aug 10 '11 at 16:41
  • @Dan : `friend` doesn't open the innards of anything -- you're explicitly allowing only a single function the same level of access it would already have if it were a member function. I.e., `friend` increases encapsulation, not decreases it. – ildjarn Aug 10 '11 at 17:15

10 Answers10

3

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.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
john
  • 85,011
  • 4
  • 57
  • 81
2

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
}
UncleBens
  • 40,819
  • 6
  • 57
  • 90
2

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

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
1

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

Nawaz
  • 353,942
  • 115
  • 666
  • 851
1

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;
Kevin
  • 24,871
  • 19
  • 102
  • 158
0

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 {
}
Juraj Blaho
  • 13,301
  • 7
  • 50
  • 96
0

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.

0

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

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • That might not be a desirable behavior in all cases. Optimization opportunities (for example a Complex class could provide faster methods for real parameters) or customizable behavior can come into play. It might seem good for the default case, however it might hide if the custom behavior isn't implemented properly. – Frigo Aug 10 '11 at 18:00
0

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

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

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.

Frigo
  • 1,709
  • 1
  • 14
  • 32