Вопрос

Существует ли канонический или рекомендуемый шаблон для реализации перегрузки арифметических операторов в числовых классах C ++?

В C ++ FAQ у нас есть оператор присваивания, исключающий исключения, который позволяет избежать большинства проблем:

class NumberImpl;

class Number {
   NumberImpl *Impl;

   ...
};

Number& Number::operator=(const Number &rhs)
{
   NumberImpl* tmp = new NumberImpl(*rhs.Impl);
   delete Impl;
   Impl = tmp;
   return *this;
}

Но для других операторов (+, + = и т. д.) дается очень мало советов, кроме как заставить их вести себя как операторы для встроенных типов.

Есть ли стандартный способ их определения? Это то, что я придумал - есть ли подводные камни, которых я не вижу?

// Member operator
Number& Number::operator+= (const Number &rhs)
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number lhs, const Number &rhs)
{
     return lhs += rhs;
}
Это было полезно?

Решение

В книге Бьярна Страуструпа Язык программирования C ++ " ;, в главе 11 (посвященной перегрузке операторов) он проходит через класс для типа комплексного числа (раздел 11.3).

В этом разделе я замечаю одну вещь: он реализует операции смешанного типа ... это, вероятно, ожидается для любого числового класса.

В общем, то, что у тебя есть, хорошо выглядит.

Другие советы

При написании любого оператора важно учитывать, что операторы-члены не преобразуются в левый параметр:

struct example {
  example(int);
  example operator + (example);
};

void foo() {
  example e(3), f(6);
  e + 4; // okay: right operand is implicitly converted to example
  e + f; // okay: no conversions needed.
  6 + e; // BAD: no matching call.
}

Это потому, что преобразование никогда не применяется к this для функций-членов, и это распространяется на операторы. Если вместо этого оператор будет example operator + (example, example) в глобальном пространстве имен, он будет скомпилирован (или если используется pass-by-const-ref).

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

Соглашение заключается в том, чтобы писать operator + (const T & amp;) и operator- (const T & amp;) в терминах operator + = (const T & amp;) и оператор - = (const T & amp;) . Если имеет смысл добавлять и вычитать в / из примитивных типов, тогда вам следует написать конструктор, который создает объект из примитивного типа. Тогда перегруженные операторы также будут работать для примитивных типов, потому что компилятор будет вызывать соответствующий конструктор неявно.

Как вы упомянули сами, вам следует избегать предоставления привилегий доступа функциям, которые в этом не нуждаются. Но в приведенном выше коде для оператора + (Number, const Number & amp;) я бы лично сделал оба параметра константными ссылками и использовал бы temp. Я думаю, это не удивительно, что комментатор под вашим вопросом пропустил это; если у вас нет веских причин не делать этого, избегайте неожиданностей и уловок и будьте как можно более очевидными.

Если вы хотите, чтобы ваш код интегрировался с другими числовыми типами, скажем, std :: complex , следите за циклическими преобразованиями. То есть не используйте оператор OtherNumeric () в Numeric , если OtherNumeric предоставляет конструктор, который принимает Numeric параметр.

Традиционно оператор X пишется в терминах оператора = X
Также традиционно все параметры для стандартных операторов являются константными

// Member operator
// This was OK
Number& Number::operator+= (Number const& rhs) 
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number const& lhs,Number const& rhs)
{
     // This I would set the lhs side to const.
     // Make a copy into result.
     // Then use += add the rhs
     Number result(lhs);
     return result += rhs;
}

Вы упомянули оператор присваивания.
Но вы не упомянули конструктор копирования. Так как ваш класс владеет указателем RAW, я ожидаю, что вы также определите это. Затем оператор присваивания традиционно записывается в терминах конструктора копирования.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top