views:

488

answers:

4

Is there a difference between defining a global operator that takes two references for a class and defining a member operator that takes only the right operand?

Global:

class X
{
public:
    int value;
};

bool operator==(X& left, X& right) 
{
    return left.value == right.value;
};

Member:

class X
{
    int value;
    bool operator==( X& right) 
    {
        return value == right.value;
    };
}
+3  A: 

There is at least one difference. A member operator is subject to access modifiers and can be public, protected or private. A global member variable is not subject to access modifier restrictions.

This is particularly helpful when you want to disable certain operators like assignment

class Foo { 
  ...
private:
  Foo& operator=(const Foo&); 
};

You could achieve the same effect by having a declared only global operator. But it would result in a link error vs. a compile error (nipick: yes it would result in a link error within Foo)

JaredPar
+10  A: 

One reason to use non-member operators (typically declared as friends) is because the left-hand side is the one that does the operation. Obj::operator+ is fine for:

obj + 2

but for:

2 + obj

it won't work. For this, you need something like:

class Obj
{
    friend Obj operator+(const Obj& lhs, int i);
    friend Obj operator+(int i, const Obj& rhs);
};

Obj operator+(const Obj& lhs, int i) { ... }
Obj operator+(int i, const Obj& rhs) { ... }
Tim Sylvester
+2  A: 

Your smartest option is to make it a friend function.

As JaredPar mentions, the global implementation cannot access protected and private class members, but there's a problem with the member function too.

C++ will allow implicit conversions of function parameters, but not an implicit conversion of this.

If types exist that can be converted to your X class:

class Y
{
public:
    operator X();  // Y objects may be converted to X
};


X x1, x2;
Y y1, y2;

Only some of the following expressions will compile with a member function.

x1 == x2;   // Compiles with both implementations
x1 == y1;   // Compiles with both implementations
y1 == x1;   // ERROR!  Member function can't convert this to type X
y1 == y2;   // ERROR!  Member function can't convert this to type X

The solution, to get the best of both worlds, is to implement this as a friend:

class X
{
    int value;

public:

    friend bool operator==( X& left, X& right ) 
    {
        return left.value == right.value;
    };
};
Shmoopty
+1  A: 

To sum up to the answer by Codebender:

Member operators are not symmetric. The compiler cannot perform the same number of operations with the left and right hand side operators.

struct Example
{
   Example( int value = 0 ) : value( value ) {}
   int value;

   Example operator+( Example const & rhs ); // option 1
};
Example operator+( Example const & lhs, Example const & rhs ); // option 2
int main()
{
   Example a( 10 );
   Example b = 10 + a;
}

In the code above will fail to compile if the operator is a member function while it will work as expected if the operator is a free function.

In general a common pattern is implementing the operators that must be member functions as members and the rest as free functions that delegate on the member operators:

class X
{
public:
   X& operator+=( X const & rhs );
};
X operator+( X lhs, X const & rhs )
{
   lhs += rhs; // lhs was passed by value so it is a copy
   return lhs;
}
David Rodríguez - dribeas