Зачем вам когда-нибудь хотеть X& x = getx();
? Просто используйте X x = getx();
и полагайтесь на RVO.
Одна из причин использования операторов, не являющихся членами (обычно объявляемых как друзья), заключается в том, что левая сторона - это та, которая выполняет операцию. 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) { ... }
Подводя итог ответу 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;
}
Самый разумный вариант - сделать его функцией друга .
Как упоминает 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;
};
};
Есть по крайней мере одно отличие. Оператор-член подлежит модификаторам доступа и может быть публичным, защищенным или частным. Глобальная переменная-член не подлежит ограничениям модификатора доступа.
Это особенно полезно, когда вы хотите отключить определенные операторы, такие как присваивание.
class Foo {
...
private:
Foo& operator=(const Foo&);
};
Вы можете добиться того же эффекта, объявив единственный глобальный оператор. Но это привело бы к ошибке ссылки вместо ошибки компиляции (nipick: да, это привело бы к ошибке ссылки в Foo)