Это слишком умный или небезопасный?

Недавно я работал над некоторым кодом и решил работать над перегрузкой моего оператора на С++, потому что я никогда не реализовал его раньше. Поэтому я перегрузил операторы сравнения для моего класса матрицы, используя функцию сравнения, которая возвращала 0, если LHS был меньше, чем RHS, 1, если LHS был больше RHS и 2, если они были равны. Затем я использовал свойства логического не в С++ для целых чисел, чтобы получить все мои сравнения в одной строке:

inline bool Matrix::operator<(Matrix &RHS){
  return ! (compare(*this,RHS));
}
inline bool Matrix::operator>(Matrix &RHS){
  return ! (compare((*this),RHS)-1);
}
inline bool Matrix::operator>=(Matrix &RHS){
  return compare((*this),RHS);
}
inline bool Matrix::operator<=(Matrix &RHS){
  return compare((*this),RHS)-1;
}
inline bool Matrix::operator!=(Matrix &RHS){
  return compare((*this),RHS)-2;
}
inline bool Matrix::operator==(Matrix &RHS){
  return !(compare((*this),RHS)-2);
}

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

В соответствии с предложением здесь приведен код, если Compare возвращает -1 для меньшего, 0 для равного и 1 для положительного.

inline bool Matrix::operator<(Matrix &RHS){
  return ! (compare(*this,RHS)+1);
}
inline bool Matrix::operator>(Matrix &RHS){
  return ! (compare((*this),RHS)-1);
}
inline bool Matrix::operator>=(Matrix &RHS){
  return compare((*this),RHS)+1;
}
inline bool Matrix::operator<=(Matrix &RHS){
  return compare((*this),RHS)-1;
}
inline bool Matrix::operator!=(Matrix &RHS){
  return compare((*this),RHS);
}
inline bool Matrix::operator==(Matrix &RHS){
  return !(compare((*this),RHS));
}

Я не знаю, что это действительно повышает читаемость.

Ответы

Ответ 1

Насколько я понимаю, это безопасно, но для всех, кто читает код, требуется дважды смотреть. Зачем вам это делать?

В любом случае, для сравнения, все, что вам когда-либо понадобится, это < и == или !=, остальное - каноническое, и я пишу его в основном из мышечной памяти. Кроме того, бинарные операторы одинаково обрабатывают свои операнды (они оставляют их в покое), если ИМО будет реализовываться как нечлены. Учитывая это, плюс использование разумной функции сравнения (-1, 0, +1) и добавление необходимого const, я прихожу к этому:

// doing real work
inline bool operator<(const Matrix& l, const Matrix &r)
{
  return -1 == compare(l,r);
}
inline bool operator==(const Matrix& l, const Matrix &r)
{
  return 0 == compare(l,r);
}
// canonical
inline bool operator> (const Matrix& l, const Matrix &r) {return r < l;}
inline bool operator>=(const Matrix& l, const Matrix &r) {return !(l < r);}
inline bool operator<=(const Matrix& l, const Matrix &r) {return !(r < l);}
inline bool operator!=(const Matrix& l, const Matrix &r) {return !(l == r);}

Сравнение может быть не таким умным, как ваше, но каждый, кто когда-либо видел strcmp(), сразу знает, что они делают. Обратите внимание, что я даже добавил 0 != compare(...), что совершенно не нужно - для компилятора. Для людей ИМО это делает более понятным, что происходит, чем неявный приведение к bool. Плюс подчеркивает симметрию реализации operator<.

Ответ 2

Да, это слишком умно - я прочитал этот код и должен подумать, почему вы вычитаете два из функции с именем compare. Не заставляйте меня думать.

Если вы умны, чтобы сделать свой код подходящим на одной строке, у вас запутались приоритеты. Вы должны использовать столько строк, сколько необходимо, чтобы сделать ваш код как можно более ясным.

Программы должны быть написаны для чтения людьми, и только случайно для машин для выполнения. (Абельсон и Суссман, структура и интерпретация компьютерных программ)

Ответ 3

Да, это слишком сложно.

compare должен возвращать 0 для равных значений, положительный, если this больше и отрицателен, если this меньше.

Это было бы намного проще и было бы даже производительности.

Если бы я дал этот код для обзора, я бы отметил это как что-то, что нужно исправить.

Ответ 4

Сравнение вывода compare to 0 с использованием любого из шести операторов сравнения даст правильный результат для соответствующего перегруженного оператора. Таким образом, ваш код будет очень читабельным, и будет немедленно очевидно, что он правильный (если compare правильно реализовано).

inline bool Matrix::operator < (const Matrix &RHS){
  return compare(*this, RHS) < 0;
}
inline bool Matrix::operator > (const Matrix &RHS){
  return compare(*this, RHS) > 0;
}
inline bool Matrix::operator >= (const Matrix &RHS){
  return compare(*this, RHS) >= 0;
}
inline bool Matrix::operator <= (const Matrix &RHS){
  return compare(*this, RHS) <= 0;
}
inline bool Matrix::operator != (const Matrix &RHS){
  return compare(*this, RHS) != 0;
}
inline bool Matrix::operator == (const Matrix &RHS){
  return compare(*this, RHS) == 0;
}

Ответ 5

Если вы беспокоитесь, что-то слишком умное... возможно, это: -)

Ответ 6

Clever?

предупреждение: см. комментарии

Я думаю, что для != для != неэффективно вызывать compare (который проверяет все значения обеих матриц, чтобы узнать, какой из них больше), потому что если m1[0][0]!=m2[0][0], то != уже может возвращать false, Поэтому я считаю хорошей идеей упростить запись этих операторов, и если производительность вообще не имеет значения, ее можно считать умной. Но если производительность имеет значение, это не умно.

Безопасно?

Я также считаю, что это безопасно, потому что он дает правильные результаты.

Ответ 7

Как упоминалось ранее, я считаю, что стандартным способом было бы вернуть < 0, когда LHS < RHS, > 0 для LHS > RHS и 0 для равенства.

Но могу ли я задать другой вопрос? Зачем вообще использовать перегрузку оператора? Идея перегрузки оператора (или должна быть), чтобы иметь возможность использовать объекты интуитивным способом. Но, насколько я знаю, нет стандартного определения для сравнения матриц, кроме равенства (in). По крайней мере, я ничего не знаю. Так что я должен думать, когда я читаю M1 < М2?

Просто позвольте мне угадать: оператор <() и operator > () были просто добавлены для полноты, но фактически не будут использоваться в реальном коде мира - правильно? Если это так, не выполняйте их.

Ответ 8

Я дам вам свой путь:

#include <boost/operators.hpp>

class Matrix: boost::equality_comparable<Matrix
            , boost::less_than_comparable<Matrix
              > >
{
}; // class Matrix

bool operator==(const Matrix&, const Matrix&);

bool operator<(const Matrix&, const Matrix&);

Я использую меньше строк, чем без трюков. Что касается Boost? Ну, это довольно стандартный к настоящему времени, и он задокументирован в Интернете. Вы всегда можете добавить комментарии:

// boost::equality_comparable: automatically generate != from ==
// boost::less_than_comparable: automatically generate >, <=, >= from <
// search for Boost.Operators on the web to get more information

Последнее слово: я не знаю о конкретном приложении, которое вы пишете, но используя матрицы, я всегда считал хорошей идеей иметь базовый класс Matrix и некоторый подкласс TMatrix (шаблон, как T указывает) с размерами, известными во время компиляции. Затем вы можете предоставить операторов на TMatrix, которые могут обрабатывать только матрицы аналогичных измерений, поскольку все остальное является ересью и, следовательно, имеет диагностику времени компиляции.