Зачем определять оператор + или + = вне класса и как это сделать правильно? - PullRequest
27 голосов
/ 11 января 2011

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

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

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

Ответы [ 3 ]

28 голосов
/ 11 января 2011

Первая форма операторов - это то, что вы определяете внутри класса 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. Прокрутите в самом конце, чтобы увидеть, как реализовать автономные функции.

Обновление:

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

Заключение

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

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

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

5 голосов
/ 11 января 2011

Правильный способ реализации операторов в отношении C ++ 03 и C ++ 0x ( NRVO и семантика перемещения):

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.Проще и безопаснее разделить его.

0 голосов
/ 11 января 2011

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

...