Что такое оператор <=> в C ++?


214

В то время как я пытался узнать о C ++ операторов, я наткнулся на операторе странного сравнения на cppreference.com , * в таблице , которая выглядит следующим образом :

введите описание изображения здесь

«Ну, если это обычные операторы в C ++, я лучше их изучу», - подумал я. Но все мои попытки выяснить эту тайну оказались безуспешными. Даже здесь, на переполнении стека, мне не повезло в поисках.

Есть ли связь между <=> и C ++ ?

И если есть, что именно делает этот оператор?

* Тем временем cppreference.com обновил эту страницу и теперь содержит информацию об <=>операторе.


82
@ haccks: О, пожалуйста, у нас было много вопросов о вещах, которые даже не были включены в стандарт. У нас есть тег C ++ 20 по причине. Такие вещи очень по теме.
— Никол Болас

1
@ cubuspl42 bar< foo::operator<=>- пример того, как это может быть похоже на <--оператора.
— Якк - Адам Невраумонт

8
@ haccks: Верно. Подобно C ++ 11 - это тег о компиляторах, которые реализуют C ++ 11. А C ++ 14 - это тег компиляторов, которые реализуют C ++ 14. А C ++ 17 - это компиляторы, которые реализуют C ++ 17. Нет, C ++ 20 - это тэг для C ++ 20. И поскольку этот вопрос касается C ++ 20, то это так. Тег вики, который был не прав, а не сам тег.
— Николь Болас

Ответы:


179

Это называется трехсторонним оператором сравнения .

Согласно предложению P0515 :

Есть новый трехсторонний оператор сравнения <=>. Выражениеa <=> b возвращает объект, который сравнивает <0if a < b, сравнивает >0if a > bи сравнивает ==0if aи bравны / эквивалентны.

Чтобы написать все сравнения для вашего типа, просто напишите, operator<=>что возвращает соответствующий тип категории:

  • Возврат к _ordering , если ваш тип естественным образом поддерживает <, и мы будем эффективно генерировать <, >, <=, >=, ==, и !=; в противном случае вернуть _равенство , и мы будем эффективно генерировать == и ! = .

  • Верните значение strong, если для вашего типа это a == bподразумевается f(a) == f(b)(подстановочность, где f читает только состояние сравнения, доступное с использованием не частного интерфейса const), в противном случае возвращает значение слабое.

Cppreference говорит:

Выражения оператора трехстороннего сравнения имеют вид

lhs <=> rhs   (1)  

Выражение возвращает объект, который

  • сравнивает <0 еслиlhs < rhs
  • сравнивает >0 еслиlhs > rhs
  • и сравнивает, ==0если lhsи rhsравны / эквивалентны.

93
Для тех, кто запутался (как я) в том , что означает «сравнивает <0», «сравнивает >0» и «сравнивает ==0», они означают, что <=>возвращает отрицательное, положительное или нулевое значение, в зависимости от аргументов. Так же, как strncmpи memcmp.
— кукурузные стебли

1
@Dai, хотя оба 'a' < 'a'и 'c' < 'a'ложные, 'a' < 'a'и 'a' < 'c'нет. В сильном порядке верно следующее: a != b→a < b || b < a
— Revolver_Ocelot

1
@Revolver_Ocelot Ах, так что это может быть определено / сгенерировано как operator==(T x, T y) { return !(x < y) && !(y < x); }и operator!=(T x, T y) { return (x < y) || (y < x); }- ах-ха! Конечно, это менее эффективно, чем истина, так ==как оно вызывает сравнение дважды, но все же аккуратно.
— Дай

3
Что означают «вернуть сильного» и «вернуть слабого»?
— lucidbrot

2
@hkBattousai это означает, что объект возвращается, когда сравнивается < 0значение true. То есть если a < bтогда (a <=> b) < 0всегда верно.
— rmobis

116

В 2017-11-11 комитет ISO C ++ принял предложение Херба Саттера о трехстороннем операторе сравнения "космический корабль" <=> в качестве одной из новых функций, добавленных в C ++ 20 . В статье под названием « Последовательное сравнение» Саттер, Маурер и Браун демонстрируют концепции нового дизайна. Для обзора предложения, вот выдержка из статьи:

Выражение a <=> b возвращает объект, который сравнивает <0, если a <b , сравнивает > 0, если a> b , и сравнивает == 0, если a и b равны / эквивалентны.

Распространенный случай: чтобы написать все сравнения для вашего типа X с типом Y с семантикой по элементам, просто напишите:

auto X::operator<=>(const Y&) =default;

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

  • Верните _ordering, если ваш тип естественным образом поддерживает < , и мы будем эффективно генерировать симметричные < , > , <= , > = , == и ! = ; в противном случае верните _equality , и мы будем эффективно генерировать симметричные == и ! = .
  • Возврат strong_ если для вашего типа A == B означает F (а) == е (б) (взаимозаменяемость, где е читает только сравнительную-выступ состояния, которое доступно с помощью общественных константные членов), в противном случае вернуть weak_ .

Категории сравнения

Пять категорий сравнения определены как std::типы, каждая из которых имеет следующие предопределенные значения:

+--------------------------------------------------------------------+
|                  |          Numeric  values          | Non-numeric |
|     Category     +-----------------------------------+             |
|                  | -1   | 0          | +1            |   values    |
+------------------+------+------------+---------------+-------------+
| strong_ordering  | less | equal      | greater       |             |
| weak_ordering    | less | equivalent | greater       |             |
| partial_ordering | less | equivalent | greater       | unordered   |
| strong_equality  |      | equal      | nonequal      |             |
| weak_equality    |      | equivalent | nonequivalent |             |
+------------------+------+------------+---------------+-------------+

Неявные преобразования между этими типами определяются следующим образом:

  • strong_orderingсо значениями { less, equal, greater} неявно преобразует в:
    • weak_orderingсо значениями { less, equivalent, greater}
    • partial_orderingсо значениями { less, equivalent, greater}
    • strong_equalityсо значениями { unequal, equal, unequal}
    • weak_equalityсо значениями { nonequivalent, equivalent, nonequivalent}
  • weak_orderingсо значениями { less, equivalent, greater} неявно преобразует в:
    • partial_orderingсо значениями { less, equivalent, greater}
    • weak_equalityсо значениями { nonequivalent, equivalent, nonequivalent}
  • partial_orderingсо значениями { less, equivalent, greater, unordered} неявно преобразует в:
    • weak_equalityсо значениями { nonequivalent, equivalent, nonequivalent, nonequivalent}
  • strong_equalityсо значениями { equal, unequal} неявно преобразуется в:
    • weak_equalityсо значениями { equivalent, nonequivalent}

Трехстороннее сравнение

<=>Маркер вводится. Последовательность символов <=>токенизируется <= >в старом исходном коде. Например, X<&Y::operator<=>необходимо добавить пробел, чтобы сохранить его значение.

Перегружаемый оператор <=>является трехсторонней функцией сравнения и имеет приоритет выше <и ниже, чем <<. Он возвращает тип, который можно сравнить с литералом, 0но допускаются и другие возвращаемые типы, например, для поддержки шаблонов выражений. Все <=>операторы, определенные на языке и в стандартной библиотеке, возвращают один из 5 вышеупомянутых std::типов категорий сравнения.

Для языковых типов предусмотрены следующие встроенные <=>сравнения одного типа. Все являются constexpr , если не указано иное. Эти сравнения не могут быть вызваны гетерогенно, используя скалярные продвижения / преобразования.

  • Для bool, целочисленный и указатель типов <=>возвращает strong_ordering.
  • Для типов указателей различные cv-квалификации и преобразования из производных в базовые могут вызывать однородные встроенные <=>, а встроенные гетерогенные operator<=>(T*, nullptr_t). Только сравнения указателей на один и тот же объект / распределение являются константными выражениями.
  • Для фундаментальных типов с плавающей запятой <=>возвращается partial_orderingи может вызываться неоднородно путем расширения аргументов до большего типа с плавающей запятой.
  • Для перечислений <=>возвращает то же, что и базовый тип перечисления <=>.
  • Для nullptr_t, <=>возвратов strong_orderingи всегда дает equal.
  • Для копируемых массивов T[N] <=> T[N]возвращает тот же тип, что Tи s, <=>и выполняет лексикографическое поэлементное сравнение. Нет <=>для других массивов.
  • Ибо voidнет <=>.

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


1
Как будто cpp уже не был достаточно сложным. Почему бы просто не написать метод сравнения ...
— Леандро

6
@Leandro Оператор космического корабля - тот метод сравнения. Кроме того, он просто работает и записывает (или удаляет) шесть других операторов сравнения. Я возьму одну операторную функцию сравнения, написанную на шести отдельных шаблонах.
— анонимно

Обратите внимание, что _equalityтипы умерли: оказалось, что <=>хорошо работает с четырьмя реляционными операторами, но не так хорошо с двумя операторами равенства (хотя есть некоторый интенсивный синтаксический сахар, поддерживающий общий случай, когда вы хотите, чтобы все они).
— Дэвис Херринг

12

Этот ответ стал неактуальным, так как ссылка на веб-страницу изменилась

Веб - страница , которую вы ссылаетесь была сломана. В тот день он много редактировался, и разные части не были синхронизированы. Статус, когда я смотрел на это был:

В верхней части страницы перечислены существующие в настоящее время операторы сравнения (в C ++ 14). Там нет <=>.

Внизу страницы они должны были перечислить тех же операторов, но они обманывают и добавляют это будущее предложение.

gccпока не знает <=>(и -std=c++14никогда не узнает ), поэтому думает, что вы имели в виду a <= > b. Это объясняет сообщение об ошибке.

Если вы попробуете то же самое через пять лет, вы, вероятно, получите лучшее сообщение об ошибке, что-то вроде <=> not part of C++14.


1
Веб-страница, на которую ссылается OP, правильная, как и отдельная страница, на которую вы ссылаетесь Он присваивает <=>оператору метку (начиная с C ++ 20), указывающую, в какой версии стандарта ее ожидать. Маркировка стандартов - это соглашение, которому следует cppreference.com. Конечно, у вас нет компилятора, который вернулся на машине времени, чтобы поддержать его для вас, но cpprefernce сообщает вам (правильно), чего ожидать.
— Спенсер

Да, но ... Не ответ. Вы комментируете ... или что-то.
— QLP

2
Я намеревался дать ссылку на ту же веб-страницу, что и вопрос, но пропустил. Я думаю, что я ответил на части вопроса, на которые другие ответы не ответили. Я проигнорировал основной жирный вопрос, так как другие уже ответили на это.
— Стиг Хеммер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.