Разница между глобальным оператором и оператором-членом
Есть ли разница между определением глобального оператора, который берет две ссылки для класса и определяет оператор-член, который принимает только правый операнд?
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;
};
}
Ответы
Ответ 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
. Но оператор равенства производного класса не может сделать обратное, отсюда и ошибка.
Если бы вместо этого оператор базового класса был объявлен как глобальная функция, оба примера сработали бы (отсутствие оператора равенства в производном классе также решило бы проблему, но иногда это необходимо).