Reputation:
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.
Upvotes: 5
Views: 417
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
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
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
Reputation: 208446
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
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
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
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
Reputation: 621
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
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
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