Существует две альтернативы для перегрузки бинарного оператора +, как свободной функции или как функции-члена. В качестве функции-члена подпись Type operator+( Type const & ) const
(const
здесь необязательна, но я предполагаю, что a+b
не изменяет a
, что кажется справедливым предположением).
Альтернативный подход - использование свободной функции, которая принимает два объекта и возвращает сумму. Для этого существуют разные подписи, но наиболее широко принятым будет: Type operator+( Type lhs, Type const & rhs )
(обратите внимание, что первый аргумент - по значению ), где реализация внутренне изменяет и возвращает lhs
. В частности, общий подход к перегрузке арифметических операторов заключается в реализации operator+=
в качестве функции-члена, а затем реализации свободной функции operator+
в терминах первого:
struct Type {
// ...
Type& operator+=( Type const & );
};
Type operator+( Type lhs, Type const & rhs ) {
return lhs+=rhs;
}
Таким образом, вам не нужно предоставлять дружбу бесплатной функции. В некоторых случаях, в частности с шаблонами, иногда рекомендуется определять оператор внутри определения класса, и в этом случае вам придется сделать его friend
(не для доступа, но по синтаксическим причинам:
struct Type {
// ...
Type& operator+=( Type const & );
// Still a free function:
friend Type operator+( Type lhs, Type const & rhs ) {
return lhs+=rhs;
}
};
По причинам использования этого шаблона ... Реализация operator+=
сначала, а затем operator+
поверх него обеспечивает две отдельные операции за небольшую дополнительную плату (operator+
- это однострочная функция!). Реализация его как функции-члена (operator+=
также может быть бесплатной функцией) делает его похожим на operator=
(должен быть членом) и устраняет необходимость в дружбе.
Причина применения operator+
в качестве свободной функции связана с симметрией типа операции. Добавление является коммутативным (a+b
== b+a
), и вы ожидаете того же при использовании вашего типа. Вы предоставили неявный конструктор (ваш сложный тип может быть неявно преобразован из int благодаря конструктору complex( double r = 0, double i = 0 )
, и это позволяет компилятору использовать эти преобразования, если вызов функции не полностью соответствует перегрузке.
Если operator+
реализован как функция-член, компилятору разрешается учитывать эту перегрузку только тогда, когда первый аргумент равен complex
, и он неявно преобразует другой аргумент, позволяя вам ввести complex(0,0)+5
. Проблема в том, что если вы измените порядок 5+complex(0,0)
, то компилятор не сможет преобразовать 5
в комплекс, а затем использовать элемент operator+
. С другой стороны, если вы предоставите ее как свободную функцию, компилятор обнаружит, что один из двух аргументов совпадает в обоих случаях, и попытается преуспеть в преобразовании другого аргумента. Конечным результатом является то, что с помощью бесплатной функции вы разрешаете в одной реализации все эти три дополнения: complex+complex
, complex+double
, double+complex
(и дополнительно для всех целочисленных типов и типов с плавающей запятой, поскольку они могут быть преобразованы в double
Если operator+
принять первый аргумент по значению, это означает, что компилятор может при некоторых обстоятельствах исключить копию: a + b + c
связывается как (a+b) + c
, временный результат первой операции может использоваться непосредственно как аргумент для второй вызов, без дополнительного копирования.