TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.36K subscribers
Post #849 4.04K
Spaceship оператор. Детали 2
#опытным

Продолжаем углубляться в космический оператор.

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

Это правда, но с оговорочками. На самом деле компилятор подменяет привычные операторы на использование spaceship оператора вот так:

SomeType a;
SomeType b;
a == b; // компилятор распознает как (a <=> b) == 0
a != b; // компилятор распознает как (a <=> b) != 0
a < b; // компилятор распознает как (a <=> b) < 0
// и тд, суть вы поняли


Ну оно и понятно. Самих операторов не завезли, приходится компилятору как ужу извиваться.

Но не всегда трехсторонний оператор предоставляет все 6 операторов.

Вгляните на пример:

struct String {
std::string str;
auto operator<=>(const String &other) const {
std::cout << "Using <=>\n";
return str <=> other.str;
}
};

String a{"foobar"};
String b{"foo"};
a == b; // Ошибка компиляции
a != b; // Ошибка компиляции
a < b; // OK
a > b; // OK
a <= b; // OK
a >= b; // OK


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

Однако казус: операторы проверки на равенство не определены, тогда как операторы определения порядка элементов нормально работают.

Почему так?

Дело в производительности.

Для всех базовых типов проблем никаких нет, для них все за нас определено. Но давайте возьмем класс вектора и попробуем для него написать оператор трехстороннего сравнения:

template<typename T>
strong_ordering operator<=>(vector<T> const& lhs,
vector<T> const& rhs)
{
size_t min_size = min(lhs.size(), rhs.size());
for (size_t i = 0; i != min_size; ++i) {
if (auto const cmp = lhs[i] <=> rhs[i]; cmp != 0) {
return cmp;
}
}
return lhs.size() <=> rhs.size();
}


Этот код работает и проходит все тесты. Однако для определения равенства этот код очень плохо перфомит. Вектора могут иметь очень длинный общий префикс, но по итогу иметь разные размеры. Логично сразу же проверить размеры векторов и, если они не равны, сразу принять решение. В рамках данного spaceship оператора это сделать невозможно(только по размерам нельзя определить отношение порядка), но стандарт нам не мешает явно определять нужные операторы:

template<typename T>
bool operator==(vector<T> const& lhs, vector<T> const& rhs)
{
// short-circuit on size early
const size_t size = lhs.size();
if (size != rhs.size()) {
return false;
}

for (size_t i = 0; i != size; ++i) {
// use ==, not <=>, in all nested comparisons
if (!(lhs[i] == rhs[i])) {
return false;
}
}

return true;
}


И это будет работать для ==. Но не будет работать для !=. Вы же помните, во что он раскрывается? В (a <=> b) != 0. Поэтому надо явно предоставлять и этот оператор. Но компилятор за нас здесь поработает и сам сгенерирует operator!=, как отрицание ==.

Получается, что если мы сами определили оператор <=>, то нас не удовлетворило базовое лексикографическое сравнение. Поэтому скорее всего нас не удовлетворит и использование такого spaceship'а для проверки на равенство. Поэтому нужно писать его отдельно. И это решает проблему с производительностью.

Хорошие новости в том, что при оборачивании такого класса в другой класс, сравнения обертки будут использовать оптимизированное сравнение, а не spaceship реализацию:
  • 👍 12
  • ❤ 9
  • 🔥 3
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →