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

214

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

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

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

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

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

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

QLP
источник
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 != ba < 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нет <=>.

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

QLP
источник
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
Я намеревался дать ссылку на ту же веб-страницу, что и вопрос, но пропустил. Я думаю, что я ответил на части вопроса, на которые другие ответы не ответили. Я проигнорировал основной жирный вопрос, так как другие уже ответили на это.
Стиг Хеммер