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?

bobobobo
  • 64,917
  • 62
  • 258
  • 363
  • 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? – CB Bailey Mar 11 '10 at 15:11
  • http://stackoverflow.com/questions/5532991/what-is-the-difference-between-overloading-an-operator-inside-or-outside-a-class also has some answers – 0fnt Feb 03 '13 at 15:43
  • For the record, in C# you can use either. "Outside class" operators (or other methods) in C# are called [extension methods](https://web.archive.org/web/20201121050945/https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods). – Michael Hoffmann Nov 24 '20 at 02:18

3 Answers3

72

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.

Azeem
  • 11,148
  • 4
  • 27
  • 40
  • 36
    So if you used the member function version, you COULD NOT have `"foo" + a`, __but you could__ have `a + "foo"` – bobobobo Mar 11 '10 at 15:01
  • @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 – Johannes Schaub - litb Mar 11 '10 at 15:22
  • There are implications for how you declare the non-member operators for templates, compare http://codepad.org/ACNeZcCf vs http://codepad.org/3jbvxRN9/. The former is called http://en.wikipedia.org/wiki/Barton-Nackman_trick. –  Mar 11 '10 at 15:23
  • @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::type` as the parameter, so it doesn't participate in deduction, but your friend function beats it all xD – Johannes Schaub - litb Mar 11 '10 at 15:26
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.

  • But if you pass `a` by reference, `Vector2& operator+=( Vector2& a, const Vector2& b )`, you could achieve the same end – bobobobo Mar 11 '10 at 15:06
  • 1
    @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=. –  Mar 11 '10 at 15:20
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".

Francesco
  • 3,200
  • 1
  • 34
  • 46