Почему неравенство тестируется как (! (A == b)) во множестве стандартного кода библиотеки С++?
Это код из кода стандартной библиотеки С++ remove
. Почему неравенство проверяется как if (!(*first == val))
вместо if (*first != val)
?
template <class ForwardIterator, class T>
ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val)
{
ForwardIterator result = first;
while (first!=last) {
if (!(*first == val)) {
*result = *first;
++result;
}
++first;
}
return result;
}
Ответы
Ответ 1
Потому что это означает, что единственным требованием T является реализация operator==
. Вы могли бы потребовать, чтобы T имел operator!=
, но общая идея здесь заключается в том, что вы должны как можно меньше нагружать пользователя шаблона, а другим шаблонам нужен operator==
.
Ответ 2
Большинство функций в STL работают только с operator<
или operator==
. Это требует от пользователя только реализовать эти два оператора (или иногда по крайней мере один из них). Например, std::set
использует operator<
(точнее std::less
, который вызывает по умолчанию operator<
), а не operator>
для управления порядком. Шаблон remove
в вашем примере аналогичен случаю - он использует только operator==
, а не operator!=
, поэтому operator!=
не нужно определять.
Ответ 3
Этот код из стандартной библиотеки С++ удаляет код.
Неправильно. Это не стандартный код библиотеки С++ remove
. Это одна возможная внутренняя реализация стандартной библиотеки С++ remove
. Стандарт С++ не предписывает действительный код; он предписывает прототипы функций и требуемое поведение.
Другими словами: С точки зрения строгого языка код, который вы видите , не существует. Это может быть из некоторого заголовочного файла, который поставляется с реализацией стандартной библиотеки вашего компилятора. Обратите внимание, что стандарт С++ даже не требует, чтобы эти файлы заголовков существовали. Файлы - это просто удобный способ для разработчиков компилятора удовлетворять требованиям для строки типа #include <algorithm>
(т.е. Создания std::remove
и других доступных функций).
Почему неравенство тестируется как if (!(*first == val))
вместо if (*first != val)
?
Потому что для функции требуется только operator==
.
Когда дело доходит до перегрузки оператора для пользовательских типов, язык позволяет вам делать всевозможные странные вещи. Вы вполне можете создать класс с перегруженным operator==
, но без перегрузки operator!=
. Или еще хуже: вы можете перегружать operator!=
, но делать это совершенно несвязанными вещами.
Рассмотрим следующий пример:
#include <algorithm>
#include <vector>
struct Example
{
int i;
Example() : i(0) {}
bool operator==(Example const& other) const
{
return i == other.i;
}
bool operator!=(Example const& other) const
{
return i == 5; // weird, but nothing stops you
// from doing so
}
};
int main()
{
std::vector<Example> v(10);
// ...
auto it = std::remove(v.begin(), v.end(), Example());
// ...
}
Если std::remove
используется operator!=
, тогда результат будет совсем другим.
Ответ 4
Некоторые хорошие ответы здесь. Я просто хотел добавить небольшую заметку.
Как и все хорошие библиотеки, стандартная библиотека разработана с (по крайней мере) двумя очень важными принципами:
-
Положите минимальную ответственность на пользователей вашей библиотеки, с которыми вы можете справиться. Отчасти это связано с тем, что при работе с вашим интерфейсом они выполняют наименьшую работу. (например, определить как можно меньше операторов, как вы можете уйти). Другая его часть связана с тем, чтобы не удивлять их или требовать от них проверки кодов ошибок (поэтому совместим интерфейсы и исключаем исключения из <stdexcept>
, когда все идет не так).
-
Устранить все логические избыточности. Все сравнения можно вывести только из operator<
, поэтому зачем требовать, чтобы пользователи определяли других? например:
(a > b) эквивалентно (b < a)
(a >= b) эквивалентно! (a < b)
(a == b) эквивалентно! ((a < b) || (b < a))
и т.д.
Конечно, в этой заметке можно спросить, почему unordered_map
требует operator==
(по крайней мере по умолчанию), а не operator<
. Ответ заключается в том, что в хэш-таблице единственное сравнение, которое мы когда-либо требуем, - это одно для равенства. Таким образом, он более логически согласован (то есть имеет смысл для пользователя библиотеки) требовать от них определения оператора равенства. Требование operator<
было бы сбивающим с толку, потому что не сразу понятно, зачем вам это нужно.
Ответ 5
В концепции EqualityComparable
требуется только operator==
.
Следовательно, любая функция, которая работает с типами, удовлетворяющими EqualityComparable
, не может полагаться на существование operator!=
для объектов этого типа. (если нет дополнительных требований, которые предполагают существование operator!=
).
Ответ 6
Наиболее перспективным подходом является поиск метода определения того, operator == можно вызвать для определенного типа, а затем поддерживать его только когда он доступен; в других ситуациях исключение будет выброшены. Однако до настоящего времени неизвестный способ определить, произвольное операторное выражение f == g соответствующим образом определено. Лучший Известный раствор имеет следующие нежелательные свойства:
- Сбой во время компиляции для объектов, где оператор == недоступен (например, поскольку он является закрытым).
- Сбой во время компиляции, если вызывающий оператор == является неоднозначным.
- Кажется правильным, если оператор == корректен, хотя оператор == не может компилироваться.
Из часто задаваемых вопросов: источник
Зная, что выполнение ==
является нагрузкой, вы никогда не захотите создавать дополнительную нагрузку, требуя также реализации !=
.
Для меня лично это о SOLID (объектно-ориентированный дизайн) L part - принцип подстановки Лискова: "объекты в программе должны быть заменены экземплярами их подтипов, не изменяя правильность этой программы.". В этом случае это оператор !=, который я могу заменить словами == и логическим обратным в логической логике.