Разница между глобальным оператором и оператором-членом - PullRequest
37 голосов
/ 17 июля 2009

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

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

Ответы [ 5 ]

45 голосов
/ 17 июля 2009

Одной из причин использования операторов, не являющихся членами (обычно объявляемых как друзья), является то, что левая сторона выполняет операцию. 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) { ... }
9 голосов
/ 18 июля 2009

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

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

C ++ допускает неявное преобразование параметров функции, но не неявное преобразование 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;
    };
};
6 голосов
/ 18 июля 2009

Подводя итог ответу 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;
}
5 голосов
/ 17 июля 2009

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

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

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

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

0 голосов
/ 07 сентября 2018

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

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. Но оператор равенства производного класса не может сделать обратное, поэтому возникает ошибка.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...