Оператор трехстороннего сравнения в С++

  Дмитрий Бушуев  | 

  |

  Обновл. 15 Сен 2021  | 

 12927

 ǀ   6 

Оператор трехстороннего сравнения (<=>) добавили в С++20, его часто еще называют «оператор-spaceship». Данный оператор для двух значений A и B определяет, является ли A < B, A = B или A > B. Вы можете сами задать оператор трехстороннего сравнения или же компилятор автоматически сгенерирует его для вас.

Чтобы оценить преимущества трехстороннего оператора сравнения, позвольте мне начать с классических примеров.

Сравнения до C++20

Я написал небольшую обертку для типа int в виде структуры MyInt. И, конечно же, я хочу сравнить переменные только что созданного типа. Ниже представлено решение, использующее шаблонную функцию isLessThan(), которая выполняет сравнение с помощью оператора <:

Результат, как и ожидалось:

isLessThan(myInt2011, myInt2014): true

Когда вы определяете одно из шести упорядочивающих отношений, то должны задать и остальные пять. А это значит, что вам каждый раз придется писать большое количество однообразного кода:

Думаете всё? Как бы ни так! А что, если теперь вы захотите сравнить MyInt с int? Для этого вам потребуется реализовать поддержку сравнения MyInt и int, а также варианта int и MyInt, вдобавок необходимо будет перегрузить каждый оператор 3 раза, т.к. наш конструктор объявлен со спецификатором explicit. Благодаря данному спецификатору неявное преобразование из int в MyInt не сработает.

Вы также можете объявить операторы в качестве дружественных функций класса, добавляя три дополнительные дружественные функции для оператора сравнения <:

Это означает, что в общей сложности вы должны реализовать 18 операторов сравнения. Похоже на конец истории? Не факт! Вдруг вы решили, что MyInt и все операторы должны стать constexpr. Значит необходимо рассмотреть возможность создания noexcept-операторов.

Я полагаю, что вы уже прочувствовали основные проблемные места вышеописанного подхода применения сравнений и теперь готовы перейти к рассмотрению оператора трехстороннего сравнения.

Сравнения в C++20


Вы можете как сами определить оператор трехстороннего сравнения, так и отдать эту работу на откуп компилятору, использовав ключевое слово default. В обоих случаях вы получите все шесть операторов сравнения: ==, !=, <, <=, > и >=.

Операторы сравнения (1) и (2) показывают ожидаемый результат своей работы:

isLessThan(myInt1, myInt2): true
isLessThan(myDouble1, myDouble2): true

Стоит отметить, что при таком подходе есть несколько нюансов. В первом варианте (1) поддерживается строгое упорядочивание, а во втором (2) — частичное упорядочивание. Числа типа с плавающей точкой поддерживают только частичное упорядочивание, поскольку значения типа с плавающей точкой, такие как NaN (англ. «Not a Number» = «Не Является Числом»), не могут быть упорядочены. Например, сравнение NaN == NaN вернет false.

Сравнение во время компиляции

Оператор трехстороннего сравнения является неявным constexpr-оператором. Поэтому я упрощу предыдущий пример threeWayComparison.cpp, выполняя сравнение при помощи функции MyDouble() на этапе компиляции:

Результат выполнения программы:

isLessThan(myDouble1, myDouble2): true

Прямое использование оператора трехстороннего сравнения


Вы можете напрямую использовать оператор трехстороннего сравнения:

Результат выполнения программы:

a < b
str1 > str2
vec1 == vec2

В вышеизложенных примерах мы увидели, насколько сообразительным может быть наш компилятор, самостоятельно генерируя все операторы сравнения.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (49 оценок, среднее: 4,82 из 5)
Загрузка...

Комментариев: 6

  1. Leon:

    Почему-то не работают операторы == и != когда сами определяем оператор трехстороннего сравнения, а если сдеать по умолчанию(default) то работают. Почему так?

  2. somebox:

    Насколько я понял, для операций сравнения >= и <= все равно придется использовать классические конструкции?

    1. Фото аватара Дмитрий Бушуев:

      Ну почему же, вот цитата из второго параграфа ("Сравнения в C++20") данного урока:
      "Вы можете как сами определить оператор трехстороннего сравнения, так и отдать эту работу на откуп компилятору, использовав ключевое слово default. В обоих случаях вы получите все шесть операторов сравнения: ==, !=, <, <=, > и >=."

  3. Peter:

    Урок классный. И ravesli тоже. В прошлом году изучал C++ по вашим с Юрой урокам. Всегда вы делаете понятные примеры и качественно подаете материал. Прекрасный интерфейс во всех частях сайта. И вроде логично ожидать от такого подхода к проекту хорошие плоды. Отдачу. Но на деле по просмотрам можно сказать что это почему то так не работает. Если взять среднее кол — во просмотров с основ крестов (примерно 75К, что очень мало за столько времени, которое прошло с появления этих уроков) и посмотреть на среднее кол — во просмотров шаблонов (примерно 20К) то можно понять, что мало того что это число почти в 4 раза меньше (т.е. каждый 4 — й уже бросил изучать кресты или просто не смотрел шаблоны, хотя это — одна их самых интересных частей). И становится грустно, наверняка, от такой вялой отдачи, которая обусловлена просто человеческой ленью. И вроде хочется сделать лучше и подарить миру что то новое в хорошей обёртке, надеясь на какой — то положительный фидбек от людей и надеясь на рост проекта, но смотря на кол — во просмотров мне кажется становится еще грустнее.
    Спасибо, что не сдаетесь и ведёте проект дальше по глубокому, бездонному, засоренному всякой фигней, интернету! 🙂
    Я надеюсь когда нибудь он взлетит в популярности. И надеюсь у вас все там хорошо.

    1. Фото аватара Юрий:

      Хорошее замечание у Вас по поводу количества просмотров.

      Дело в том, что, действительно, многие из тех, кто начинает учить С++ по урокам Равесли — не доходит до конца (думаю, что даже больше половины таких). Я считаю, что это нормально. Сам я прошел все уроки в оригинале только на 3-й раз, т.е. с перерывами. 240+ уроков — это не 20+ уроков, и нужно потратить не день, не неделю и, скорее всего, даже не месяц, чтобы пройти всё до конца. И нужно быть реалистами, тема программирования — это специфическая тема, не предназначенная для всех вокруг. Тема С++ — это еще более узкоспециализированная тема, поэтому нет ничего удивительного в том, что каждый второй на улице не проходит уроки по С++ на Равесли, невзирая на то, как бы хорошо эти уроки не были бы написаны.

      Второй нюанс количества просмотров зависит от поисковых запросов. Многие из тех, кто просмотрели определенные уроки, нашли их через Гугл/Яндекс по конкретному запросу. Они не искали целенаправленно уроки по С++ для начинающих, а, допустим, искали, как использовать switch в С++, что такое циклы, геттеры и сеттеры и т.д. Им нужен был конкретный ответ на конкретный вопрос в данный момент и всё. Поэтому есть перепады в количестве просмотров разных уроков. Те, у которых просмотров больше — значит более популярны, проще и интересуют большее количество людей (например, циклы, класс, ООП, string и т.д.), нежели узкотематические (символьные константы строк C-style, ёмкость вектора, контейнерные классы, перегрузка оператора индексации и т.д.).

      Чтобы не было грустно за фидбек, какой бы он ни был — нужно делать в первую очередь то, что, либо нравится тебе, либо не вызывает у тебя отторжение при игре в долгую. Если при этом еще и что-то "выстреливает", то это круто :))

      1. Аноним:

        Согласен с каждым словом

Добавить комментарий для Peter Отменить ответ

Ваш E-mail не будет опубликован. Обязательные поля помечены *