Определение правильного оператора вычитания - PullRequest
2 голосов
/ 27 июня 2009

Я написал класс абстракции для математического объекта и определил все операторы. При использовании я наткнулся на:

Fixed f1 = 5.0f - f3;

У меня определены только два оператора вычитания:

inline const Fixed operator - () const;
inline const Fixed operator - (float f) const;

Я понял, что здесь не так - сложение можно заменить (1 + 2 == 2 + 1), а вычитание - нет (то же самое относится и к умножению и делению). Я сразу написал функцию за пределами моего класса так:

static inline const Fixed operator - (float f, const Fixed &fp);

Но потом я понял, что этого нельзя сделать, потому что для этого мне нужно будет прикоснуться к личным лицам класса, что приводит к использованию ключевого слова friend, которое я ненавижу, а также к загрязнению пространства имен «статическим» ненужным функция.

Перемещение функции внутри определения класса приводит к этой ошибке в gcc-4.3:

error: ‘static const Fixed Fixed::operator-(float, const Fixed&)’ must be either a non-static member function or a non-member function

Выполнение в соответствии с предложением GCC и превращение его в нестатическую функцию приводит к следующей ошибке:

error: ‘const Fixed Fixed::operator-(float, const Fixed&)’ must take either zero or one argument

Почему я не могу определить один и тот же оператор внутри определения класса? если нет способа сделать это, в любом случае не использовать ключевое слово friend?

Тот же вопрос касается деления, поскольку он страдает от той же проблемы.

Ответы [ 4 ]

3 голосов
/ 27 июня 2009

Если вам нужно убедиться, что функции друзей могут быть в порядке:

http://www.gotw.ca/gotw/084.htm

К каким операциям требуется доступ внутренние данные мы бы иначе имели подарить через дружбу? Это должно обычно быть членами. (Есть некоторые редкие исключения, такие как операции нуждающихся в преобразованиях на их левой руке аргументы и некоторые как оператор << () чьи подписи не позволяют * это ссылка, чтобы быть их первым параметры; даже они обычно могут быть не-друзья реализованы с точки зрения (возможно, виртуальные) участники, но иногда это просто упражнение в искривлении, и они лучше всего и естественно выражается как друзья.) </p>

Вы находитесь в лагере "Операции, требующие преобразования на левых аргументах". Если вам не нужен друг и предполагается, что у вас есть неявный конструктор float для Fixed, вы можете реализовать его следующим образом:

static inline Fixed operator-(const Fixed &lhs, const Fixed &rhs) {
    return lhs.minus(rhs);
}

затем реализует minus как общедоступную функцию-член, с которой большинство пользователей не будут беспокоиться, потому что они предпочитают оператора.

Я предполагаю, что если у вас есть operator-(float), то у вас есть operator+(float), поэтому, если у вас нет оператора преобразования, вы можете использовать:

static inline Fixed operator-(float lhs, const Fixed &rhs) {
    return (-rhs) + lhs;
    // return (-rhs) -(-lhs); if no operator+...
}

Или просто Fixed(lhs) - rhs, если у вас есть явный float конструктор. Они могут быть или не быть такими же эффективными, как реализация вашего друга.

К сожалению, язык не собирается наклоняться назад, чтобы приспособиться к тем, кто случайно ненавидит одно из его ключевых слов, поэтому операторы не могут быть статическими функциями-членами и таким образом получать эффекты дружбы; -p

1 голос
/ 27 июня 2009
  1. "Это то, что друзья для ..."
  2. Вы можете добавить неявное преобразование между float и вашим типом (например, с помощью конструктора, принимающего float) ... но я думаю, что использование friend лучше.
0 голосов
/ 27 июня 2009

Как правило, операторы свободных функций для арифметических операций лучше, чем реализации функций-членов. Основной причиной является проблема, с которой вы сталкиваетесь сейчас. Компилятор будет обрабатывать левую и правую стороны по-разному. Обратите внимание, что хотя строгие OO-последователи будут рассматривать только те методы внутри класса, которые являются фигурными скобками, частью его интерфейса, но эксперты утверждают, что это не так в C ++.

Если оператору бесплатной функции требуется доступ к закрытым членам, сделайте друга оператором. В конце концов, если он предоставлен в том же заголовочном файле (следуя объяснению Саттера выше), то он является частью класса.

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

class Fixed {
private:
   Fixed();
   Fixed( double d ); // implicit conversion to Fixed from double

   Fixed substract( Fixed const & rhs ) const;
// ...
};

Fixed operator-( Fixed const & lhs, Fixed const & rhs )
{
   return lhs.substract( rhs );
}

В приведенном выше коде вы можете вычесть Fixed - Fixed, Fixed - double, double - Fixed. Компилятор найдет свободную функцию и неявно преобразует (в примере через конструктор double) двойные числа в Fixed объекты.

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

// idiomatic polymorphic dump operator
class Base {
public:
   virtual std::ostream& dump( std::ostream & ) const;
};
std::ostream& operator<<( std::ostream& o, Base const & d )
{
   return d.dump( o );
}
0 голосов
/ 27 июня 2009

Когда вы определяете что-то вроде этого,

inline const Fixed operator - (float f) const;

вы говорите, что я хочу, чтобы этот оператор (вы находитесь внутри класса) работал с определенным типом, например, с плавающей точкой.

В то время как дружественный бинарный оператор означает операцию между двумя типами.

class Fixed
{
    inline friend const Fixed operator-(const Fixed& first, const float& second);
};

inline const Fixed operator-(const Fixed& first, const float& second)
{
    // Your definition here.
}

с дружественными операторами вы можете иметь свой класс по обе стороны от самого оператора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...