Подтвердить что ты не робот

Зачем определять оператор + или + = вне класса и как это сделать правильно?

Я немного смущен различиями между

Type  operator +  (const Type &type);
Type &operator += (const Type &type);

и

friend Type  operator +  (const Type &type1, const Type &type2);
friend Type &operator += (const Type &type1, const Type &type2);

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

4b9b3361

Ответ 1

Первая форма операторов - это то, что вы определяете внутри класса Type.

Вторая форма операторов - это то, что вы определяете как свободностоящие функции в том же пространстве имен, что и класс Type.

Это очень хорошая идея для определения автономных функций, потому что тогда операнды к ним могут участвовать в неявных преобразованиях.

Пример

Предположим, что этот класс:

class Type {
    public:
    Type(int foo) { }

    // Added the const qualifier as an update: see end of answer
    Type operator + (const Type& type) const { return *this; }
};

Затем вы можете написать:

Type a = Type(1) + Type(2); // OK
Type b = Type(1) + 2; // Also OK: conversion of int(2) to Type

Но вы не можете писать:

Type c = 1 + Type(2); // DOES NOT COMPILE

Имея operator+ как свободную функцию, также позволяет и последний случай.

То, что вторая форма оператора делает неправильно, заключается в том, что она выполняет добавление путем прямой настройки частных членов своих операндов (я предполагаю, что в противном случае это не обязательно должно быть другом). Это не должно быть так: вместо этого операторы также должны быть определены внутри класса, и автономные функции должны их называть.

Чтобы узнать, как это получится, попросите услуги гуру: http://www.gotw.ca/gotw/004.htm. Прокрутите в самом конце, чтобы увидеть, как реализовать автономные функции.

Update:

Как отмечает Джеймс Макнеллис в своем комментарии, две приведенные формы также имеют другое значение: левая сторона не является const-квалификацией в первой версии. Так как операнды operator+ действительно не должны быть модифицированы как часть добавления, очень хорошая идея, чтобы const-квалифицировать их все время. Класс Type в моем примере теперь делает это, где изначально этого не было.

Заключение

Лучший способ справиться с операторами + и +=:

  • Определите operator+= как T& T::operator+=(const T&); внутри вашего класса. Здесь будет добавлено дополнение.
  • Определите operator+ как T T::operator+(const T&) const; внутри вашего класса. Этот оператор будет реализован в терминах предыдущего.
  • Предоставьте бесплатную функцию T operator+(const T&, const T&); вне класса, но внутри одного и того же пространства имен. Эта функция вызовет элемент operator+ для выполнения этой работы.

Вы можете опустить шаг 2 и напрямую запросить функцию T::operator+=, но в качестве личного предпочтения я хочу сохранить всю логику добавления внутри класса.

Ответ 2

Правильный способ реализации операторов относительно С++ 03 и С++ 0x (NRVO и move-semantics), является

struct foo
{
    // mutates left-operand => member-function
    foo& operator+=(const foo& other)
    {
        x += other.x;

        return *this;
    }

    int x;
};

// non-mutating => non-member function
foo operator+(foo first, // parameter as value, move-construct (or elide)
                const foo& second) 
{
    first += second; // implement in terms of mutating operator

    return first; // NRVO (or move-construct)
}

Обратите внимание на соблазн сочетать приведенное выше:

foo operator+(foo first, const foo& second) 
{
    return first += second;
}

Но иногда (в моем тестировании) компилятор не включает NRVO (или перемещает семантику), потому что он не может быть уверенным (пока он не вставит оператор мутирования), что first += second совпадает с first. Проще и безопаснее разделить его.

Ответ 3

Спецификатор friend используется, когда объявляемая вещь не является членом класса, но нуждается в доступе к закрытым членам экземпляров класса для выполнения своей работы.

Если ваш оператор будет определен в самом классе, используйте первый способ; если это будет отдельная функция, используйте вторую.