Ir para o conteúdo

Última atualização:2026-10-12

Operador de comparação de três vias <=> no C++20

Três tipos de ordenação

O operador de comparação de três vias <=>, também chamado de operador spaceship (nave espacial), possui três tipos de ordenação como valores de retorno; a seguir, vamos nos aprofundar sob a ótica do código-fonte do gcc.

Obs.: versão para quem tem pressa, o exemplo desta seção já foi atualizado no Planeta.

Uso: <=>

#include <compare>
#include <iostream>

int main() {
   auto c1 = 1.1 <=> 2.2;
   auto c2 = -1 <=> 1;
   std::cout << typeid(c1).name() << std::endl;
   std::cout << typeid(c2).name() << std::endl;
}

Saída:

St16partial_ordering
St15strong_ordering
  1. 1.1 <=> 2.2: esta é uma comparação entre dois números de ponto flutuante. Para números de ponto flutuante, a biblioteca padrão retorna std::partial_ordering, pois a existência de valores NaN faz com que a comparação entre eles não seja uma ordem total estrita. std::partial_ordering permite que o resultado seja <, >, == ou unordered.

Podemos usar as variáveis estáticas do código do gcc para ver se o partial_ordering é menor, maior, igual ou não ordenado:

static const partial_ordering less;
static const partial_ordering equivalent;
static const partial_ordering greater;
static const partial_ordering unordered;

Se forçarmos a conversão, obteremos um erro:

comp.cc:6:36: error: conversion from 'std::partial_ordering' to non-scalar type 'std::strong_ordering' requested
    6 |   std::strong_ordering c1 = 1.1 <=> 2.2;
2. `-1 <=> 1`: esta é uma comparação entre dois inteiros. Para inteiros, a biblioteca padrão retorna `std::strong_ordering`, pois a comparação de inteiros é uma relação de ordem total estrita, sem casos indeterminados. `std::strong_ordering` só retorna `<`, `>` ou `==`.

De forma análoga, podem-se usar os 4 membros estáticos a seguir.

static const strong_ordering less;
static const strong_ordering equal;
static const strong_ordering equivalent;
static const strong_ordering greater;

Portanto, o tipo de c1 é std::partial_ordering, enquanto o de c2 é std::strong_ordering, o que também explica por que os tipos impressos são diferentes.

Além de partial_ordering e strong_ordering, <=> também pode retornar weak_ordering, cujos membros estáticos são:

static const weak_ordering less;
static const weak_ordering equivalent;
static const weak_ordering greater;

equal e equivalent

Nas três ordenações acima, vê-se que para a igualdade aparecem duas: equal e equivalent. Qual é a diferença entre elas?

Aqui, consultando diretamente o cppreference: não há diferença entre elas. No entanto, std::strong_ordering é a única categoria que define igualdade (equality). Todas as outras categorias definem apenas equivalência (equivalence). Isso confirma exatamente os membros estáticos vistos acima: para string_ordering há dois, e para as demais há apenas equivalent.

Se a comparação produz uma ordenação forte entre os itens comparados, então equivalência e igualdade são a mesma coisa. A razão de existirem duas palavras para isso é que, para os outros tipos de ordenação, não é assim. Ordenações fracas e parciais simplesmente não têm igualdade; elas fornecem apenas equivalência.

Equivalência significa que dois objetos podem comparar-se como iguais. Igualdade significa algo mais forte: se eles comparam iguais, um pode substituir o outro em qualquer uso const; desde que a == b seja verdadeiro, vale a propriedade de que f(a) == f(b), em que f denota uma função que apenas lê o estado saliente comparável acessível por meio dos membros const públicos do argumento. Se a comparação de um tipo permite igualdade (que é um requisito da ordenação forte), ela também permite equivalência. Portanto, para comparações com ordenação forte, elas são a mesma coisa.

https://en.cppreference.com/w/cpp/utility/compare/strong_ordering

Por que usar o operador de comparação de três vias?

O operador de comparação de três vias (<=>) permite determinar a relação de ordenação entre dois valores em uma única operação, enquanto os operadores de comparação tradicionais exigem múltiplas comparações.

Limitações dos operadores de comparação tradicionais:

  • Se a == b é falso, você não sabe se a < b ou a > b.
  • Se a != b é verdadeiro, você não sabe se a < b ou a > b.
  • Se a < b é falso, você não sabe se a == b ou a > b.
  • Se a > b é falso, você não sabe se a == b ou a < b.
  • Se a <= b é verdadeiro, você não sabe se a == b ou a < b.
  • Se a >= b é verdadeiro, você não sabe se a == b ou a > b.

Essas limitações mostram que os operadores de comparação tradicionais não conseguem determinar de uma só vez a relação completa entre dois valores: são necessárias múltiplas comparações para obter o resultado completo.

Fim desta seção

Tópicos relacionados

Comentários