79

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?

3
  • 1
    Did you mean for the member operator+ to be non-const as this gives the non-member function a head start in 'correcterness' as it will work in more situations? Commented Mar 11, 2010 at 15:11
  • stackoverflow.com/questions/5532991/… also has some answers Commented Feb 3, 2013 at 15:43
  • For the record, in C# you can use either. "Outside class" operators (or other methods) in C# are called extension methods. Commented Nov 24, 2020 at 2:18

3 Answers 3

75

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.

Sign up to request clarification or add additional context in comments.

4 Comments

So if you used the member function version, you COULD NOT have "foo" + a, but you could have a + "foo"
@Roger, aww you are fast. I already deleted my comment right away since it wasn't on-topic. Your barton-nackman trickery looks neat, didn't think of that at all xD
There are implications for how you declare the non-member operators for templates, compare codepad.org/ACNeZcCf vs codepad.org/3jbvxRN9. The former is called en.wikipedia.org/wiki/Barton-Nackman_trick.
@Roger, now this comment thread looks fun. Some reverse_iterator rushed over it :) I agree, this is a problem with std::string too. You can't do std::string s; s = s + +'a'; and expect 'a' to be added since the second argument expects CharT but it deduces to int oO I was about to recommend identity<CharT>::type as the parameter, so it doesn't participate in deduction, but your friend function beats it all xD
24

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.

2 Comments

But if you pass a by reference, Vector2& operator+=( Vector2& a, const Vector2& b ), you could achieve the same end
@bobobobo: Op+= often needs access to non-public members, and in that case it's easier to make it a member than a friend. Op= must be a member and it's also convenient to group +=, -=, etc. with op=.
6

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.