Подтвердить что ты не робот

Разница между глобальным оператором и оператором-членом

Есть ли разница между определением глобального оператора, который берет две ссылки для класса и определяет оператор-член, который принимает только правый операнд?

Global:

class X
{
public:
    int value;
};

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

Статус:

class X
{
    int value;
    bool operator==( X& right) 
    {
        return value == right.value;
    };
}
4b9b3361

Ответ 1

Одна из причин использования операторов, не являющихся членами (обычно объявленных друзьями), заключается в том, что левая сторона является той, которая выполняет операцию. Obj::operator+ подходит для:

obj + 2

но для:

2 + obj

это не сработает. Для этого вам нужно что-то вроде:

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) { ... }

Ответ 2

Ваш самый умный вариант - сделать его функцией друга.

Как упоминает JaredPar, глобальная реализация не может получить доступ к защищенным и закрытым членам класса, но также существует проблема с функцией-членом.

С++ допускает неявное преобразование параметров функции, но не неявное преобразование this.

Если существуют типы, которые могут быть преобразованы в ваш класс X:

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


X x1, x2;
Y y1, y2;

Только некоторые из следующих выражений будут компилироваться с помощью функции-члена.

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

Решение, чтобы получить лучшее из обоих миров, заключается в реализации этого как друга:

class X
{
    int value;

public:

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

Ответ 3

Подводя итог ответу Codebender:

Операторы-члены не симметричны. Компилятор не может выполнять такое же количество операций с операциями левой и правой стороны.

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;
}

В приведенном выше коде не будет компилироваться, если оператор является функцией-членом, пока он будет работать как ожидалось, если оператор является свободной функцией.

В общем, общий шаблон реализует операторы, которые должны быть функциями-членами в качестве членов, а остальные - свободными функциями, которые делегируют на операторах-членах:

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;
}

Ответ 4

Существует по крайней мере одно различие. Оператор-член имеет доступ к модификаторам доступа и может быть общедоступным, защищенным или закрытым. Глобальная переменная-член не подпадает под ограничения модификатора доступа.

Это особенно полезно, если вы хотите отключить определенные операторы, такие как назначение

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

Вы можете добиться такого же эффекта, имея объявленный только глобальный оператор. Но это приведет к ошибке связи с ошибкой компиляции (nipick: yes, это приведет к ошибке связи внутри Foo)

Ответ 5

Вот реальный пример, где разница не очевидна:

class Base
{
public:
    bool operator==( const Base& other ) const
    {
        return true;
    }
};

class Derived : public Base
{
public:
    bool operator==( const Derived& other ) const
    {
        return true;
    }
};

Base() == Derived(); // works
Derived() == Base(); // error

Это потому, что первая форма использует оператор равенства из базового класса, который может преобразовать свою правую часть в Base. Но оператор равенства производного класса не может сделать обратное, отсюда и ошибка.

Если бы вместо этого оператор базового класса был объявлен как глобальная функция, оба примера сработали бы (отсутствие оператора равенства в производном классе также решило бы проблему, но иногда это необходимо).