Это слишком умный или небезопасный?
Недавно я работал над некоторым кодом и решил работать над перегрузкой моего оператора на С++, потому что я никогда не реализовал его раньше. Поэтому я перегрузил операторы сравнения для моего класса матрицы, используя функцию сравнения, которая возвращала 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
, которые могут обрабатывать только матрицы аналогичных измерений, поскольку все остальное является ересью и, следовательно, имеет диагностику времени компиляции.