This question already has answers here:
Closed 2 years ago.
There are two ways to overload operators for a C++ class:
Inside class
class Vector2
{
public:
float x, y ;
Vector2 operator+( const Vector2 & other )
{
Vector2 ans ;
ans.x = x + other.x ;
ans.y = y + other.y ;
return ans ;
}
} ;
Outside class
class Vector2
{
public:
float x, y ;
} ;
Vector2 operator+( const Vector2& v1, const Vector2& v2 )
{
Vector2 ans ;
ans.x = v1.x + v2.x ;
ans.y = v1.y + v2.y ;
return ans ;
}
(Apparently in C# you can only use the "outside class" method.)
In C++, which way is more correct? Which is preferable?
The basic question is "Do you want conversions to be performed on the left-hand side parameter of an operator?". If yes, use a free function. If no, use a class member.
For example, for operator+()
for strings, we want conversions to be performed so we can say things like:
string a = "bar";
string b = "foo" + a;
where a conversion is performed to turn the char * "foo"
into an std::string
. So, we make operator+()
for strings into a free function.
First: the two different ways are really "overload as a member" and "overload as a non-member", and the latter has two different ways to write it (as-friend-inside class definition and outside class definition). Calling them "inside class" and "outside class" is going to confuse you.
Overloads for +=, +, -=, -, etc. have a special pattern:
struct Vector2 {
float x, y;
Vector2& operator+=(Vector2 const& other) {
x += other.x;
y += other.y;
return *this;
}
Vector2& operator-=(Vector2 const& other) {
x -= other.x;
y -= other.y;
return *this;
}
};
Vector2 operator+(Vector2 a, Vector2 const& b) {
// note 'a' is passed by value and thus copied
a += b;
return a;
}
Vector2 operator-(Vector2 a, Vector2 const& b) { return a -= b; } // compact
This pattern allows the conversions mentioned in the other answers for the LHS argument while simplifying the implementation considerably. (Either member or non-member allows conversions for the RHS when it's passed either as a const&
or by value, as it should be.) Of course, this only applies when you do actually want to overload both += and +, -= and -, etc., but that is still common.
Additionally, you sometimes want to declare your non-member op+, etc. as friends within the class definition using the Barton-Nackman trick, because due to quirks of templates and overloading, it may not be found otherwise.
There is an excellent discussion of this issue in Meyer's Effective C++: Item 24 is "Declare non-member functions when type conversions should apply to all parameters" and Item 46 is "Define non-member functions inside templates when type conversions are desired".