O que é o operador <=> em C ++?

215

Enquanto eu estava tentando aprender sobre C ++ operadores, me deparei com um operador de comparação estranha em cppreference.com , * em uma tabela que ficou assim:

insira a descrição da imagem aqui

"Bem, se esses são operadores comuns em C ++, é melhor eu aprendê-los", pensei. Mas todas as minhas tentativas de elucidar esse mistério foram infrutíferas. Mesmo aqui, no Stack Overflow, não tive sorte na minha pesquisa.

Existe uma conexão entre <=> e C ++ ?

E, se houver, o que esse operador faz exatamente?

* Enquanto isso, o cppreference.com atualizou essa página e agora contém informações sobre o <=>operador.

qlp
fonte
82
@haccks: Oh, por favor, tivemos muitas perguntas sobre coisas que nem foram votadas no padrão. Temos uma tag C ++ 20 por um motivo. Esse tipo de coisa é bastante abordada no tópico.
Nicol Bolas
1
@ cubuspl42 bar< foo::operator<=>é um exemplo de como pode ser como o <--operador.
Yakk - Adam Nevraumont
8
@haccks: Certo. Like C ++ 11 é uma tag sobre compiladores que implementam C ++ 11. E C ++ 14 é uma tag sobre compiladores que implementam C ++ 14. E o C ++ 17 trata de compiladores que implementam o C ++ 17. Não, o C ++ 20 é a tag para coisas sobre o C ++ 20. E como essa pergunta é sobre C ++ 20, existe. O wiki da tag que estava errado, não a própria tag.
Nicol Bolas

Respostas:

180

Isso é chamado de operador de comparação de três vias .

De acordo com a proposta do documento P0515 :

Há um novo operador de comparação de três vias <=>,. A expressão a <=> bretorna um objeto que compara <0se a < b, compara >0se a > be compara ==0se ae bé igual / equivalente.

Para escrever todas as comparações para o seu tipo, basta escrever operator<=>que retorna o tipo de categoria apropriado:

  • Retornar um _ordering Se o seu tipo apoia naturalmente <, e vamos eficientemente gerar <, >, <=, >=, ==, e !=; caso contrário, retorne uma _equality e geraremos eficientemente == e ! = .

  • Retorno forte se para o seu tipo a == bimplica f(a) == f(b)(substituibilidade, onde f lê apenas um estado relevante para comparação acessível usando a interface const não privada), caso contrário, retorne fraco.

A cppreference diz:

As expressões de operador de comparação de três vias têm o formato

lhs <=> rhs   (1)  

A expressão retorna um objeto que

  • compara <0selhs < rhs
  • compara >0selhs > rhs
  • e compara ==0se lhse rhssão iguais / equivalentes.
msc
fonte
93
Para aqueles que estão confusos (como eu estava) sobre o que "compara <0", "compara >0" e "compara ==0" significa, eles significam que o <=>retorno é um valor negativo, positivo ou zero, dependendo dos argumentos. Muito parecido strncmpe memcmp.
precisa
1
@ Dai, embora ambos 'a' < 'a'e ambos 'c' < 'a'sejam falsos, 'a' < 'a'e 'a' < 'c'não são. Em ordem forte, o seguinte é verdadeiro: a != ba < b || b < a
Revolver_Ocelot
1
@Revolver_Ocelot Ah, então ela pode ser definida / gerado como operator==(T x, T y) { return !(x < y) && !(y < x); }e operator!=(T x, T y) { return (x < y) || (y < x); }- ah-ha! É claro que isso é menos eficiente do que verdadeiro ==, pois invoca a comparação duas vezes, mas ainda é puro.
Dai
3
O que significam "retorno forte" e "retorno fraco"?
Lucidbrot #
2
@hkBattousai significa que o objeto retorna, quando comparado < 0com true. Ou seja, se a < bentão (a <=> b) < 0é sempre verdade.
Rmobis
116

Em 11/11/2017 , o comitê ISO C ++ adotou a proposta de Herb Sutter para o operador de comparação tripartida <=> "nave espacial" como um dos novos recursos adicionados ao C ++ 20 . No artigo intitulado Comparação consistente Sutter, Maurer e Brown demonstram os conceitos do novo design. Para uma visão geral da proposta, aqui está um trecho do artigo:

A expressão a <=> b retorna um objeto que compara <0 se a <b , compara > 0 se a> b e compara == 0 se a e b são iguais / equivalentes.

Caso comum: para escrever todas as comparações para o seu tipo X com o tipo Y , com semântica de membro, basta escrever:

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

Casos avançados: para escrever todas as comparações para o seu tipo X com o tipo Y , basta escrever o operador <=> que usa um Y , pode usar = default para obter semântica de membro, se desejar, e retornar o tipo de categoria apropriado:

  • Retorne um _ordering se seu tipo suportar naturalmente < , e geraremos eficientemente simétricos < , > , <= , > = , == e ! = ; caso contrário, retorne uma _equality e geraremos eficientemente == e ! = simétricos .
  • Retorne forte_ se para o seu tipo a == b implica f (a) == f (b) (substituibilidade, onde f lê apenas o estado saliente da comparação que é acessível usando os membros públicos const ), caso contrário, retorne fraco_ .

Categorias de comparação

Cinco categorias de comparação são definidas como std::tipos, cada um com os seguintes valores predefinidos:

+--------------------------------------------------------------------+
|                  |          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 |             |
+------------------+------+------------+---------------+-------------+

Conversões implícitas entre esses tipos são definidas da seguinte maneira:

  • strong_orderingcom valores { less, equal, greater} converte implicitamente:
    • weak_orderingcom valores { less, equivalent, greater}
    • partial_orderingcom valores { less, equivalent, greater}
    • strong_equalitycom valores { unequal, equal, unequal}
    • weak_equalitycom valores { nonequivalent, equivalent, nonequivalent}
  • weak_orderingcom valores { less, equivalent, greater} converte implicitamente:
    • partial_orderingcom valores { less, equivalent, greater}
    • weak_equalitycom valores { nonequivalent, equivalent, nonequivalent}
  • partial_orderingcom valores { less, equivalent, greater, unordered} converte implicitamente:
    • weak_equalitycom valores { nonequivalent, equivalent, nonequivalent, nonequivalent}
  • strong_equalitycom valores { equal, unequal} converte implicitamente em:
    • weak_equalitycom valores { equivalent, nonequivalent}

Comparação de três vias

O <=>token é introduzido. A sequência de caracteres <=>simboliza para <= >, no código-fonte antigo. Por exemplo, X<&Y::operator<=>precisa adicionar um espaço para manter seu significado.

O operador sobrecarregável <=>é uma função de comparação de três vias e tem precedência maior que <e menor que <<. Ele retorna um tipo que pode ser comparado com literal, 0mas outros tipos de retorno são permitidos, como suporte a modelos de expressão. Todos os <=>operadores definidos no idioma e na biblioteca padrão retornam um dos 5 std::tipos de categorias de comparação mencionados acima .

Para tipos de idiomas, <=>são fornecidas as seguintes comparações do mesmo tipo internas. Todos são constexpr , exceto onde indicado de outra forma. Essas comparações não podem ser chamadas de forma heterogênea usando promoções / conversões escalares.

  • Para booltipos de integrante e ponteiro, <=>retorna strong_ordering.
  • Para tipos de ponteiros, as diferentes qualificações de cv e conversões de derivadas para base podem invocar um built-in homogêneo <=>e existem heterogêneos embutidos operator<=>(T*, nullptr_t). Somente comparações de ponteiros com o mesmo objeto / alocação são expressões constantes.
  • Para tipos de ponto flutuante fundamentais, <=>retorna partial_orderinge pode ser chamado de forma heterogênea, ampliando argumentos para um tipo de ponto flutuante maior.
  • Para enumerações, <=>retorna o mesmo que o tipo subjacente da enumeração <=>.
  • Pois nullptr_t, <=>retorna strong_orderinge sempre produz equal.
  • Para matrizes copiáveis, T[N] <=> T[N]retorna o mesmo tipo que T's <=>e executa comparação lexicográfica elemento a elemento. Não há <=>para outras matrizes.
  • Pois voidnão há <=>.

Para entender melhor o funcionamento interno deste operador, leia o documento original . Isso é exatamente o que eu descobri usando os mecanismos de pesquisa.

qlp
fonte
1
Como se o cpp já não fosse suficientemente complexo. Por que não simplesmente escrever um método de comparação ...
Leandro
6
@Leandro O operador da nave espacial é esse método de comparação. Além disso, ele simplesmente funciona e grava (ou exclui) os outros seis operadores de comparação. Vou usar uma função de operador de comparação escrita em seis clichês individuais.
anónimo
Observe que os _equalitytipos morreram: resultou que <=>funciona bem com os quatro operadores relacionais, mas não com os dois operadores de igualdade (embora exista algum açúcar sintático intenso para apoiar o caso comum em que você deseja todos eles).
Davis Herring
12

Esta resposta se tornou irrelevante desde que a página da Web referenciada mudou

A página da web que você está referenciando estava quebrada. Ele estava sendo editado muito naquele dia e partes diferentes não estavam sincronizadas. O status quando eu estava olhando era:

Na parte superior da página, lista os operadores de comparação existentes no momento (em C ++ 14). Não existe <=>lá.

Na parte inferior da página, eles deveriam ter listado os mesmos operadores, mas enganaram e adicionaram esta sugestão futura.

gccainda não sabe <=>(e com -std=c++14, nunca saberá), então pensa que você quis dizer a <= > b. Isso explica a mensagem de erro.

Se você tentar a mesma coisa daqui a cinco anos, provavelmente receberá uma mensagem de erro melhor, algo como <=> not part of C++14.

Stig Hemmer
fonte
1
O link do OP da página da web está correto, assim como a página separada à qual você vincula. Ele qualifica o <=>operador com o rótulo (desde C ++ 20), informando em qual versão do padrão o esperado. O rótulo de padrões é uma convenção que cppreference.com segue. É claro que você não tem um compilador que voltou em uma máquina do tempo para dar suporte a você, mas o cpprefernce diz (corretamente) o que esperar.
Spencer #
Sim, mas ... Não é uma resposta. Você está comentando ... ou algo assim.
Qlp
2
Eu pretendia vincular à mesma página da Web da pergunta, mas perdi. Acho que respondi às partes da pergunta que outras respostas não. Eu ignorei a principal questão em negrito, pois outros já haviam respondido a isso.
amigos estão dizendo sobre