Почему неравенство тестируется как (! (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 - принцип подстановки Лискова: "объекты в программе должны быть заменены экземплярами их подтипов, не изменяя правильность этой программы.". В этом случае это оператор !=, который я могу заменить словами == и логическим обратным в логической логике.