Увидел недавно оператор <=> в чужом коде и испытал культурный шок. Что за зверь, что он делает и когда использовать?
#include <iostream>
#include <compare>
int main() {
int a, b = a = 42;
std::cout << (a <=> b == 0);
}
Такой оператор известен как "spaceship operator" или космический корабль или оператор трехстороннего сравнения (так его официально называют), чаще всего я слышу первый вариант. Я этот оператор первый раз увидел на Perl и вроде как это был первый язык который использовал его, после чего он начал появляться в Ruby, PHP, C++ и т.д.
Этот оператор предназначен для сравнения двух выражений или значений. Запомнить как он работает, очень просто: Возвращается целочисленное значение -1, 0 или 1 если a, соответственно, меньше, равно или больше чем b. Все просто и очевидно, минус значит меньше, 0 значит нет разницы, 1 больше. Со стороны программного кода это эквивалент:
if a<b
return -1
elsif a>b
return 1
else
return 0
end
Набор таких возвращаемых значений уже давно известен, примерно с таким же успехом для строк работает функция strcmp или memcmp.
Очень удобно использовать данный оператор для реализаций всяких сортировок, ведь если писать какую-нибудь функцию сравнения и сортировки числового массива нужно 3 значения (больше, меньше, равно), так как значений bool (0,1) недостаточно.
Если вы когда либо писали операторы сравнения в более-менее тяжелых C++ классах, то вы, наверное, замечали, что вся идея построения системы сравнений вокруг классических операторов <
, >
, ==
и т.д. и/или вокруг соотношения "less" ("меньше"), на который опираются упорядочивающие алгоритмы стандартной библиотеки неудобна и дико неэффективна. Не существует приемлемого/эффективного способа реализации упорядочивающих соотношений для составных объектов на основе существующих соотношений для их индивидуальных компонентов.
Например, для класса
class Composite
{
Type1 a;
Type2 b;
Type3 c;
};
лексикографическое сравнение <
будет выглядеть примерно так
bool operator <(const Composite &lhs, const Composite &rhs)
{
if (lhs.a != rhs.a)
return lhs.a < rhs.a;
if (lhs.b != rhs.b)
return lhs.b < rhs.b;
return lhs.c < rhs.c;
}
Это, разумеется, катастрофически плохо, ибо такой вариант на некотором шаге выполняет фактически одно и то же сравнение два раза. К примеру, сначала делается lhs.a != rhs.a
, а затем lhs.a < rhs.a
, то по сути является повторным выполнением одной и той же (возможно тяжелой) операции.
Чтобы решить эту проблему, в С++20 предложили новую парадигму реализации сравнений: в качестве основы выступают "трехсторонние" ("3-way") сравнения, хорошо знакомые нам еще из C. Теперь фундаментальным примитивом упорядочивающего сравнения для каждого типа данных будет оператор <=>
, выполняющий трехстороннее сравнение. Все остальные виды сравнения будут неявно генерироваться на основе результата оператора <=>
.
Например, на основе оператора <=>
вышеприведенный скетч кода может быть переписан так
bool operator <(const Composite &lhs, const Composite &rhs)
{
if (auto cmp = lhs.a <=> rhs.a; cmp != 0)
return cmp < 0;
if (auto cmp = lhs.b <=> rhs.b; cmp != 0)
return cmp < 0;
if (auto cmp = lhs.c <=> rhs.c; cmp != 0)
return cmp < 0;
return false;
}
(Пример приведен в иллюстративных целях. На самом деле правильнее будет реализовать именно и только оператор <=>
для класса Composite
и дать компилятору сгенерировать оператор <
на его основе.)
Так что ответ на ваш вопрос про "когда использовать": никогда и всегда. В коде верхнего уровня он почти никогда не нужен. В вашем примере нет никакого повода использовать <=>
вместо обычного ==
. А вот в качестве лежащей под всем этим основы для реализации операторов сравнения он нужен почти всегда. Без него вы просто не сможете построить приемлемой реализации лексикографического сравнения для составного объекта. В этой роли большинство из нас уже давно руками выписывали аналог этого оператора в виде какой-то функции. А теперь эта практика будет закреплена на уровне языка.
Также в стандартной библиотеке С++20 появится вспомогательная функция std::compare_3way
, реализующая трехсторонние сравнения на базе обычных сравнений.
Это называется трехсторонним оператором сравнения.
Согласно предложению P0515:
Появился новый трехсторонний оператор сравнения <=>. Выражение a <=> b возвращает объект, который сравнивает <0, если a <b, сравнивает> 0, если a> b, и сравнивает == 0, если a и b равны / эквивалентны.
Чтобы написать все сравнения для вашего типа, просто напишите оператор <=>, который возвращает соответствующий тип категории:
Верните _ordering, если ваш тип естественным образом поддерживает <, и мы будем эффективно генерировать <,>, <=,> =, == и! =; в противном случае верните _equality, и мы будем эффективно генерировать == и! =.
Возвращает значение strong, если для вашего типа a == b подразумевает f (a) == f (b) (подстановочность, где f читает только состояние сравнения, доступное с использованием не частного интерфейса const), в противном случае возвращает значение слабое.
Совпадение говорит:
Выражения оператора трехстороннего сравнения имеют вид
lhs <=> rhs (1)
Выражение возвращает объект, который
сравнивает <0, если lhs <rhs
сравнивает> 0, если lhs> rhs
и сравнивает == 0, если lhs и rhs равны / эквивалентны.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Не первый раз ошибка *** wasnt declared in this scope, так и не могу понять в чем дело
Необходимо определить размер массива, переданного в функциюПробовал вот так:
Почему конструкция switch case работает только с интегральными типами, и что вообще такое интегральные типы?
Всех приветствуюНаткнулся на пример отправки изображения камеры с клиента на сервер, решил попробовать отослать изображение рабочего стола...