If I want to overload operator +
, which prototype is correct?
D operator+(const D& lhs, const D& rhs);
then declare it as a friend function of D.D operator+(const D& s);
Then declare it as a member function of D.
If I want to overload operator +
, which prototype is correct?
D operator+(const D& lhs, const D& rhs);
then declare it as a friend function of D.
D operator+(const D& s);
Then declare it as a member function of D.
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.
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
}
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
Go with the first one. However, if it needs to access private members,only then make it friend
, otherwise make it non-friend function.
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;
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 {
}
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.
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).
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.)
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.