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


215

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

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

"Ну, якщо це звичайні оператори в C ++, я краще їх вивчу", - подумав я. Але всі мої спроби з’ясувати цю таємницю були безуспішними. Навіть тут, на Stack Overflow, мені не пощастило в пошуку.

Чи існує зв’язок між <=> і 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, воно є. Вікі з тегами, що було неправильно, а не сам тег.
Ніколь Болас

Відповіді:


180

Це називається оператором тристороннього порівняння .

Відповідно до пропозиції P0515 :

Там новий тристоронній оператор порівняння, <=>. Вираз a <=> bповертає об'єкт, який порівнює, <0якщо a < b, порівнює, >0якщо a > bі порівнює, ==0якщо aі bдорівнює / еквівалент.

Щоб написати всі порівняння для вашого типу, просто напишіть, operator<=>що повертає відповідний тип категорії:

  • Повернення до _ordering , якщо ваш тип природним чином підтримує <, і ми будемо ефективно генерувати <, >, <=, >=, ==, і !=; інакше повернемо _рівність , і ми будемо ефективно генерувати == і ! = .

  • Повернення сильне, якщо для вашого типу 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.
Cornstalks

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оцінюється з істинним. Тобто, якщо a < bтоді (a <=> b) < 0завжди правда.
rmobis

116

На 2017-11-11 , Комітет з ISO C ++ прийнятий Herb Sutter пропозиції «s для <=> оператора порівняння триходового" космічного корабля " в якості одного з нових функцій , які були додані в C ++ 20 . У статті під назвою Послідовне порівняння Саттер, Маурер та Браун демонструють концепції нового дизайну. Огляд пропозиції, ось уривок із статті:

Вираз a <=> b повертає об'єкт, який порівнює <0, якщо a <b , порівнює > 0, якщо a> b , і порівнює == 0, якщо a і b рівні / еквівалентні.

Поширений випадок: щоб написати всі порівняння для вашого типу X із типом Y із семантиками, що належать до членів, просто напишіть:

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

Розширені випадки. Щоб написати всі порівняння для типу X із типом Y , просто напишіть оператор <=>, який приймає Y , за допомогою цього пункту можна використовувати = за замовчуванням, щоб отримати семантику членства, і поверне відповідний тип категорії:

  • Повертає _ordering , якщо ваш тип природним чином підтримує < , і ми будемо ефективно генерувати симетричний < , > , <= , > = , == і ! = ; інакше повернемо _рівність , і ми будемо ефективно генерувати симетричні == і ! = .
  • Повертайтеся strong_, якщо для вашого типу a == b означає f (a) == f (b) (замінюваність, де f читає лише стан порівняння, який доступний за допомогою публічних членів const ), інакше поверніть слабкий_ .

Порівняльні категорії

П'ять категорій порівняння визначаються як 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.