Перегрузка оператора внешним классом
Существует два способа перегрузить операторы для класса С++:
Внутренний класс
class Vector2
{
public:
float x, y ;
Vector2 operator+( const Vector2 & other )
{
Vector2 ans ;
ans.x = x + other.x ;
ans.y = y + other.y ;
return ans ;
}
} ;
Внешний класс
class Vector2
{
public:
float x, y ;
} ;
Vector2 operator+( const Vector2& v1, const Vector2& v2 )
{
Vector2 ans ;
ans.x = v1.x + v2.x ;
ans.y = v1.y + v2.y ;
return ans ;
}
(По-видимому, в С# вы можете использовать только метод "внешнего класса".)
В С++, какой путь является более правильным? Что предпочтительнее?
Ответы
Ответ 1
Основной вопрос: "Вы хотите, чтобы преобразования выполнялись по левому параметру оператора?". Если да, используйте свободную функцию. Если нет, используйте член класса.
Например, для оператора +() для строк мы хотим, чтобы выполнялись преобразования, поэтому мы можем сказать такие вещи, как:
string a = "bar";
string b = "foo" + a;
где выполняется преобразование, чтобы превратить char * "foo" в std::string. Поэтому мы приводим оператор +() для строк в свободную функцию.
Ответ 2
Во-первых: два разных способа - это действительно "перегрузка как член" и "перегрузка как нечлен", а вторая имеет два разных способа ее записи (определение класса "внутри друга" и определение внешнего класса), Вызов их "внутри класса" и "внешнего класса" будет вас путать.
Перегрузки для + =, +, - =, - и т.д. имеют специальный шаблон:
struct Vector2 {
float x, y;
Vector2& operator+=(Vector2 const& other) {
x += other.x;
y += other.y;
return *this;
}
Vector2& operator-=(Vector2 const& other) {
x -= other.x;
y -= other.y;
return *this;
}
};
Vector2 operator+(Vector2 a, Vector2 const& b) {
// note 'a' is passed by value and thus copied
a += b;
return a;
}
Vector2 operator-(Vector2 a, Vector2 const& b) { return a -= b; } // compact
Этот шаблон позволяет переделки, упомянутые в других ответах для аргумента LHS, значительно упрощая реализацию. (Любой член или не-член разрешает конверсии для RHS, когда он передал либо как const&
, либо как значение, как и должно быть.) Конечно, это применимо только тогда, когда вы действительно хотите перегрузить как + =, так и + - = и - и т.д., но это все еще распространено.
Кроме того, вы иногда хотите объявить свой не-член op + и т.д. в качестве друзей в определении класса с помощью трюк Barton-Nackman, потому что из-за причуд шаблонов и перегрузки он не может быть найден иначе.
Ответ 3
В Meyer Effective С++ есть прекрасное обсуждение этой проблемы: в пункте 24 "Объявлять функции, не являющиеся членами, когда преобразования типов должны применяться ко всем параметрам", а в пункте 46 "Определять функции, не являющиеся членами внутри шаблонов, когда преобразования типов желательно".