Я изучаю перегрузку операторов в C ++ и вижу, что ==
а также !=
это просто некоторые специальные функции, которые можно настроить для пользовательских типов. Моя проблема, однако, почему там два отдельных определения нужны? Я думал, что если a == b
верно, то a != b
автоматически ложно, и наоборот, и нет другой возможности, потому что, по определению, a != b
является !(a == b)
, И я не мог представить себе ситуацию, в которой это было бы неправдой. Но, может быть, мое воображение ограничено или я чего-то не знаю?
Я знаю, что могу определить одно в терминах другого, но я не об этом. Я также не спрашиваю о различии между объектами по значению или идентичности. Или могут ли два объекта быть равными и не равными одновременно (это определенно не вариант! Эти вещи взаимоисключающие). Я спрашиваю о следующем:
Возможна ли ситуация, когда задавать вопросы о равенстве двух объектов имеет смысл, но спрашивать о них? не быть равным не имеет смысла? (либо с точки зрения пользователя, либо с точки зрения исполнителя)
Если такой возможности нет, то почему в C ++ эти два оператора определены как две разные функции?
Ты бы не хотите, чтобы язык автоматически переписывался a != b
как !(a == b)
когда a == b
возвращает что-то кроме bool
, И есть несколько причин, почему вы могли бы сделать это.
У вас могут быть объекты построителя выражений, где a == b
не и не предназначен для выполнения какого-либо сравнения, а просто создает некоторый узел выражения, представляющий a == b
,
Вы можете иметь ленивую оценку, где a == b
не и не предназначен для непосредственного сравнения, но вместо этого возвращает lazy<bool>
которые могут быть преобразованы в bool
неявно или явно в более позднее время, чтобы фактически выполнить сравнение. Возможно в сочетании с объектами построителя выражений для полной оптимизации выражений перед оценкой.
Вы можете иметь некоторые обычай optional<T>
шаблон класса, где заданы необязательные переменные t
а также u
хочешь разрешить t == u
, но верни optional<bool>
,
Вероятно, есть еще кое-что, о чем я не думал. И хотя в этих примерах операция a == b
а также a != b
оба имеют смысл, все еще a != b
не то же самое, что !(a == b)
, поэтому необходимы отдельные определения.
Если такой возможности нет, то почему в C ++ эти два оператора определены как две разные функции?
Потому что вы можете перегружать их, и, перегружая их, вы можете придать им совершенно иной смысл, чем их первоначальный.
Взять, к примеру, оператора <<
Первоначально оператор побитового сдвига влево, теперь обычно перегруженный как оператор вставки, как в std::cout << something
; совершенно другой смысл от оригинального.
Таким образом, если вы принимаете, что значение оператора изменяется при его перегрузке, то нет никаких причин препятствовать пользователю давать значение оператору. ==
это не совсем отрицание оператора !=
хотя это может сбивать с толку.
Однако меня беспокоит, почему нужны два отдельных определения?
Вам не нужно определять оба.
Если они взаимоисключающие, вы можете быть краткими, только определяя ==
а также <
рядом СТД :: rel_ops
Соотношение:
#include <iostream>
#include <utility>
struct Foo {
int n;
};
bool operator==(const Foo& lhs, const Foo& rhs)
{
return lhs.n == rhs.n;
}
bool operator<(const Foo& lhs, const Foo& rhs)
{
return lhs.n < rhs.n;
}
int main()
{
Foo f1 = {1};
Foo f2 = {2};
using namespace std::rel_ops;
//all work as you would expect
std::cout << "not equal: : " << (f1 != f2) << '\n';
std::cout << "greater: : " << (f1 > f2) << '\n';
std::cout << "less equal: : " << (f1 <= f2) << '\n';
std::cout << "greater equal: : " << (f1 >= f2) << '\n';
}
Возможна ли ситуация, когда задают вопросы о двух
Равные объекты имеют смысл, но спрашивать о том,
равно не имеет смысла?
Мы часто связываем эти операторы с равенством.
Хотя именно так они ведут себя на фундаментальных типах, не обязательно, чтобы это было их поведение на пользовательских типах данных.
Вам даже не нужно возвращать бул, если вы этого не хотите.
Я видел, как люди перезагружали операторов причудливыми способами, но обнаружил, что это имеет смысл для их доменных приложений. Даже если интерфейс показывает, что они являются взаимоисключающими, автор может захотеть добавить определенную внутреннюю логику.
(либо с точки зрения пользователя, либо с точки зрения исполнителя)
Я знаю, что вы хотите конкретный пример,
так вот один из Поймать рамки тестирования что я думал, было практично:
template<typename RhsT>
ResultBuilder& operator == ( RhsT const& rhs ) {
return captureExpression<Internal::IsEqualTo>( rhs );
}
template<typename RhsT>
ResultBuilder& operator != ( RhsT const& rhs ) {
return captureExpression<Internal::IsNotEqualTo>( rhs );
}
Эти операторы делают разные вещи, и не имеет смысла определять один метод как! (Не) другого. Причина, по которой это сделано, заключается в том, что платформа может распечатать сделанное сравнение. Для этого необходимо захватить контекст того, какой перегруженный оператор был использован.
Есть несколько очень устоявшихся конвенций, в которых (a == b)
а также (a != b)
являются оба ложные не обязательно противоположности. В частности, в SQL любое сравнение с NULL дает NULL, а не true или false.
Вероятно, не очень хорошая идея создавать новые примеры этого, если это вообще возможно, потому что это не интуитивно понятно, но если вы пытаетесь смоделировать существующее соглашение, было бы хорошо иметь возможность заставить ваши операторы вести себя «правильно» для этого контекст.
Я отвечу только на вторую часть вашего вопроса, а именно:
Если такой возможности нет, то почему в C ++ эти два оператора определены как две разные функции?
Одной из причин, по которой имеет смысл разрешить разработчику перегружать оба, является производительность. Вы можете разрешить оптимизацию путем реализации обоих ==
а также !=
, затем x != y
может быть дешевле, чем !(x == y)
является. Некоторые компиляторы могут оптимизировать его для вас, но, возможно, нет, особенно если у вас есть сложные объекты с большим количеством ветвлений.
Даже в Хаскеле, где разработчики очень серьезно относятся к законам и математическим концепциям, все же разрешается перегружать оба ==
а также /=
, как вы можете видеть здесь (http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude.html#v:-61—61-):
$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
λ> :i Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
-- Defined in `GHC.Classes'
Вероятно, это будет считаться микрооптимизацией, но в некоторых случаях это может быть оправдано.
Возможна ли ситуация, когда задают вопросы о двух
Равные объекты имеют смысл, но спрашивать о том,
равно не имеет смысла? (либо с точки зрения пользователя, либо
Перспектива исполнителя)
Это мнение. Может быть, это не так. Но разработчики языка, не будучи всезнающими, решили не ограничивать людей, которые могут придумывать ситуации, в которых это может иметь смысл (по крайней мере, для них).
В ответ на редактирование;
То есть, если для некоторого типа возможно иметь оператор
==
но не!=
или наоборот, и когда имеет смысл это делать.
В генеральный, нет, это не имеет смысла. Равенство и реляционные операторы обычно бывают множествами. Если есть равенство, то и неравенство; меньше чем, то больше чем и так далее с <=
и т. д. Аналогичный подход применяется и к арифметическим операторам, они также обычно бывают естественными логическими множествами.
Это подтверждается в std::rel_ops
Пространство имен. Если вы реализуете операторы равенства и меньше чем, использование этого пространства имен дает вам другие, реализованные в терминах ваших оригинальных реализованных операторов.
Это все сказано, Существуют ли условия или ситуации, когда одно не может сразу означать другое или не может быть реализовано в терминах других? да это так, возможно, мало, но они есть; опять же, как свидетельствует rel_ops
быть собственным пространством имен. По этой причине, позволяя им быть реализованными независимо, вы можете использовать язык, чтобы получить семантику, которая вам требуется или нужна, таким образом, который все еще естественен и интуитивен для пользователя или клиента кода.
Упомянутая ленивая оценка уже является отличным примером этого. Другим хорошим примером является предоставление им семантики, которая вовсе не означает равенство или неравенство. Примером этого являются операторы сдвига битов <<
а также >>
используется для вставки и извлечения потока. Хотя в общих кругах это может вызывать недовольство, в некоторых областях, относящихся к конкретной области, это может иметь смысл.
Если ==
а также !=
операторы на самом деле не подразумевают равенство, так же, как <<
а также >>
Операторы потока не подразумевают сдвиг битов. Если вы относитесь к символам так, как будто они означают какую-то другую концепцию, они не должны быть взаимоисключающими.
С точки зрения равенства может иметь смысл, если ваш сценарий использования требует рассматривать объекты как несопоставимые, так что каждое сравнение должно возвращать false (или несопоставимый тип результата, если ваши операторы возвращают не bool). Я не могу придумать конкретную ситуацию, в которой это оправдано, но я вижу, что это достаточно разумно.