Я надеюсь, что это не дубликат — я искал другие вопросы относительно операторов равенства, но кроме некоторых комментариев в связанных вопросах, я не нашел определенного утверждения
ТЛ; др:
Гарантирует ли стандарт C ++, что (x!=y)
всегда имеет то же значение истинности, что и !(x==y)
?
Я знаю, что есть много Здесь задействованы тонкости: операторы ==
а также !=
может быть перегружен. Они могут быть перегружены, чтобы иметь разные типы возврата (которые должны быть неявно преобразованы в bool
). Даже !
-оператор может быть перегружен в типе возврата. Вот почему я неукоснительно ссылался на «значение истины» выше, но пытаясь уточнить его, используя неявное преобразование в bool
и пытается устранить возможные неясности:
bool ne = (x!=y);
bool e = (x==y);
bool result = (ne == (!e));
Является result
гарантированно будет true
Вот?
Стандарт C ++ определяет операторы равенства в разделе 5.10, но в основном, кажется, определяет их синтаксически (и некоторая семантика относительно сравнения указателей). концепция быть EqualityComparable существует, но нет специального заявления об отношениях его оператора ==
к !=
оператор.
Существуют соответствующие документы из рабочих групп C ++, говоря это…
Важно, чтобы равные / неравные […] вели себя как булевы отрицания друг друга. В конце концов, мир не будет иметь смысла, если оба оператора == () и оператор! = () Вернули false! Таким образом, эти операторы часто используются в терминах друг друга.
Тем не менее, это только отражает здравый смысл ™, и не уточнить что они должны быть реализованы так.
Немного предыстории: я просто пытаюсь написать функцию, которая проверяет, равны ли два значения (неизвестного типа), и выводить сообщение об ошибке, если это не так. Я хотел бы сказать, что необходимая концепция здесь заключается в том, что типы EqualityComparable
, Но для этого все равно придется писать if (!(x==y)) {...}
и мог не записывать if (x!=y) {...}
потому что это будет использовать другой оператор, который не охватывается концепцией EqualityComparable
вообще, и который может быть даже перегружен по-другому …
Я знаю, что программист в основном Можно делать все, что он хочет в его пользовательских перегрузок. Мне просто интересно, действительно ли он позволил делать все или есть ли правила, навязанные стандартом. Может быть, одно из этих тонких утверждений, предполагающих, что отклонение от обычной реализации вызывает неопределенное поведение, например, такое, которое NathanOliver упоминается в комментарии, но, похоже, относится только к определенным типам. Например, стандарт эксплицитно утверждает, что для типы контейнеров, a!=b
эквивалентно !(a==b)
(раздел 23.2.1, таблица 95, «Требования к контейнерам»).
Но для общих пользовательских типов в настоящее время кажется, что таких требований нет. Вопрос помечен language-lawyer
потому что я надеялся на определенное утверждение / ссылку, но я знаю, что это может быть почти невозможно: хотя можно указать на раздел, где говорится, что операторы иметь чтобы быть отрицанием друг друга, вряд ли можно доказать, что ни одна из ~ 1500 страниц стандарта не говорит что-то подобное …
Сомневаюсь, и если не будет дальнейших намеков, я буду голосовать / принимать соответствующие ответы позже, а пока предположим, что для сравнения неравенства для EqualityComparable
типы должны быть сделаны с if (!(x==y))
быть на безопасной стороне.
Гарантирует ли стандарт C ++, что
(x!=y)
всегда имеет то же значение истинности, что и!(x==y)
?
Нет, это не так. Абсолютно ничто не мешает мне писать:
struct Broken {
bool operator==(const Broken& ) const { return true; }
bool operator!=(const Broken& ) const { return true; }
};
Broken x, y;
Это совершенно правильно сформированный код. Семантически, он сломан (как следует из названия), но в этом нет ничего плохого с точки зрения функциональности чистого кода C ++.
Стандарт также четко указывает, что это нормально в [Over.oper] / 7:
Тождества среди определенных предопределенных операторов, применяемых к базовым типам (например,
++a ≡ a+=1
) не нужно держать для операторских функций. Некоторые предопределенные операторы, такие как+=
требовать, чтобы операнд был lvalue применительно к базовым типам; это не требуется операторскими функциями.
В том же духе, ничто в стандарте C ++ не гарантирует, что operator<
на самом деле реализует действительный порядок (или что x<y <==> !(x>=y)
, так далее.). Некоторые реализации стандартной библиотеки фактически добавят инструментарий, чтобы попытаться отладить это для вас в упорядоченных контейнерах, но это только проблема качества реализации, а не решение, основанное на стандартах.
Библиотечные решения, такие как повысить операторы существует, по крайней мере, сделать это немного проще на стороне программиста:
struct Fixed : equality_comparable<Fixed> {
bool operator==(const Fixed&) const;
// a consistent operator!= is provided for you
};
Хотя сейчас Fixed
больше не может быть совокупностью. Так что это все еще не идеальное решение. Хоть P0017 очевидно был принят для C ++ 17, что позволило бы инициализацию списка для Fixed
,
В общем, я не думаю, что вы можете положиться на это, потому что это не всегда имеет смысл для operator ==
а также operator!=
в всегда соответствуют, так что я не вижу, как стандарт мог когда-либо требовать этого.
Например, рассмотрим встроенные типы с плавающей запятой, такие как double, для которых NaNs всегда сравнивайте false, так что operator == и operator! = могут возвращать false одновременно. (Редактировать: Ой, это неправильно; см. Комментарий hvd.)
В результате, если я пишу новый класс с семантикой с плавающей запятой (возможно, действительно_long_double), я иметь реализовать то же поведение, чтобы соответствовать примитивным типам, поэтому мой operator==
придется вести себя одинаково и сравнивать два NaN как ложные, даже если operator!=
также сравнивает их как ложные.
Это может возникнуть и в других обстоятельствах. Например, если бы я писал класс для представления обнуляемого значения базы данных, я мог бы столкнуться с той же проблемой, поскольку все сравнения с базой данных NULL являются ложными. Я мог бы реализовать эту логику в своем коде C ++, чтобы иметь ту же семантику, что и база данных.
На практике, однако, для вашего варианта использования, возможно, не стоит беспокоиться об этих крайних случаях. Просто документируйте, что ваша функция сравнивает объекты, используя operator== (or operator !=)
и оставить все как есть.
Нет. Вы можете написать перегрузки оператора для ==
а также !=
делай что хочешь. Это, вероятно, было бы плохой идеей, но определение C ++ не ограничивает эти операторы логическими противоположностями друг друга.