¿Cuál es el operador <=> en C ++?

215

Mientras intentaba aprender sobre los operadores de C ++ , me topé con un extraño operador de comparación en cppreference.com , * en una tabla que se veía así:

ingrese la descripción de la imagen aquí

"Bueno, si estos son operadores comunes en C ++, será mejor que los aprenda", pensé. Pero todos mis intentos de dilucidar este misterio no tuvieron éxito. Incluso aquí, en Stack Overflow, no tuve suerte en mi búsqueda.

¿Hay alguna conexión entre <=> y C ++ ?

Y si lo hay, ¿qué hace exactamente este operador?

* Mientras tanto, cppreference.com actualizó esa página y ahora contiene información sobre el <=>operador.

qlp
fuente
82
@haccks: Oh, por favor, hemos tenido muchas preguntas sobre cosas que ni siquiera se votaron en el estándar. Tenemos una etiqueta C ++ 20 por una razón. Este tipo de cosas está muy relacionado con el tema.
Nicol Bolas
1
@ cubuspl42 bar< foo::operator<=>es un ejemplo de cómo podría ser como el <--operador.
Yakk - Adam Nevraumont
8
@haccks: Correcto. Como C ++ 11 es una etiqueta sobre compiladores que implementan C ++ 11. Y C ++ 14 es una etiqueta sobre compiladores que implementan C ++ 14. Y C ++ 17 se trata de compiladores que implementan C ++ 17. No, el C ++ 20 es la etiqueta para cosas sobre C ++ 20. Y como esta pregunta es sobre C ++ 20, ahí está. La etiqueta wiki que estaba mal, no la etiqueta en sí.
Nicol Bolas

Respuestas:

180

Esto se llama el operador de comparación de tres vías .

De acuerdo con la propuesta de papel P0515 :

Hay un nuevo operador de comparación de tres vías, <=>. La expresión a <=> bdevuelve un objeto que compara <0if a < b, compara >0if a > by compara ==0if ay bson iguales / equivalentes.

Para escribir todas las comparaciones para su tipo, solo escriba operator<=>que devuelva el tipo de categoría apropiado:

  • Devolver un _ordering Si el tipo de soportes, naturalmente <, y vamos a generar de manera eficiente <, >, <=, >=, ==, y !=; de lo contrario, devolverá una _equality , y generaremos eficientemente == y ! = .

  • Regrese fuerte si para su tipo a == bimplica f(a) == f(b)(sustituibilidad, donde f lee solo el estado relevante de comparación accesible usando la interfaz const no privada), de lo contrario, devuelva débil.

La cppreference dice:

Las expresiones de operador de comparación de tres vías tienen la forma

lhs <=> rhs   (1)  

La expresión devuelve un objeto que

  • compara <0silhs < rhs
  • compara >0silhs > rhs
  • y compara ==0si lhsy rhsson iguales / equivalentes.
msc
fuente
93
Para aquellos que están confundidos (como yo) sobre lo que significa "compara <0", "compara >0" y "compara ==0", significan que <=>devuelve un valor negativo, positivo o cero, según los argumentos. Al igual que strncmpy memcmp.
Cornstalks
1
@Dai a pesar de que ambos 'a' < 'a'y ambos 'c' < 'a'son falsos, 'a' < 'a'y 'a' < 'c'no lo son. EN ORDENAMIENTO SIGUIENTE lo siguiente es cierto: a != ba < b || b < a
Revolver_Ocelot
1
@Revolver_Ocelot Ah, por lo que se puede definir / generar como operator==(T x, T y) { return !(x < y) && !(y < x); }y operator!=(T x, T y) { return (x < y) || (y < x); }- ¡ah-ha! Por supuesto, esto es menos eficiente que un verdadero, ==ya que invoca la comparación dos veces, pero sigue siendo ordenado.
Dai
3
¿Qué significa "volver fuerte" y "volver débil"?
lucidbrot
2
@hkBattousai significa que el objeto regresa, cuando se compara se < 0evalúa como verdadero. Es decir, si a < bentonces (a <=> b) < 0siempre es cierto.
rmobis
116

El 11-11-2017 , el comité ISO C ++ adoptó la propuesta de Herb Sutter para el operador de comparación de tres vías <=> "nave espacial" como una de las nuevas características que se agregaron a C ++ 20 . En el artículo titulado Comparación consistente Sutter, Maurer y Brown demuestran los conceptos del nuevo diseño. Para obtener una descripción general de la propuesta, aquí hay un extracto del artículo:

La expresión a <=> b devuelve un objeto que compara <0 si a <b , compara > 0 si a> b , y compara == 0 si a y b son iguales / equivalentes.

Caso común: para escribir todas las comparaciones para su tipo X con el tipo Y , con la semántica de miembro, simplemente escriba:

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

Casos avanzados: para escribir todas las comparaciones para su tipo X con el tipo Y , simplemente escriba el operador <=> que toma una Y , puede usar = default para obtener la semántica de miembro si lo desea, y devuelve el tipo de categoría apropiado:

  • Devuelva una orden si su tipo admite naturalmente < , y generaremos eficientemente < , > , <= , > = , == y ! = ; de lo contrario, devolverá una _equality y generaremos eficientemente simétrica == y ! = .
  • Devuelva strong_ si para su tipo a == b implica f (a) == f (b) (sustituibilidad, donde f lee solo el estado relevante de comparación al que se puede acceder utilizando los miembros de const público ), de lo contrario, devuelve débil_ .

Categorías de comparación

Cinco categorías de comparación se definen como std::tipos, cada una con los siguientes 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 |             |
+------------------+------+------------+---------------+-------------+

Las conversiones implícitas entre estos tipos se definen de la siguiente manera:

  • strong_orderingcon valores { less, equal, greater} convierte implícitamente a:
    • weak_orderingcon los valores { less, equivalent, greater}
    • partial_orderingcon los valores { less, equivalent, greater}
    • strong_equalitycon los valores { unequal, equal, unequal}
    • weak_equalitycon los valores { nonequivalent, equivalent, nonequivalent}
  • weak_orderingcon valores { less, equivalent, greater} convierte implícitamente a:
    • partial_orderingcon los valores { less, equivalent, greater}
    • weak_equalitycon los valores { nonequivalent, equivalent, nonequivalent}
  • partial_orderingcon valores { less, equivalent, greater, unordered} convierte implícitamente a:
    • weak_equalitycon los valores { nonequivalent, equivalent, nonequivalent, nonequivalent}
  • strong_equalitycon valores { equal, unequal} se convierte implícitamente en:
    • weak_equalitycon valores { equivalent, nonequivalent}

Comparación de tres vías

Se <=>introduce el token. La secuencia de caracteres se <=>tokeniza <= >en el código fuente anterior. Por ejemplo, X<&Y::operator<=>necesita agregar un espacio para retener su significado.

El operador sobrecargable <=>es una función de comparación de tres vías y tiene prioridad mayor que <y menor que <<. Devuelve un tipo que se puede comparar con el literal, 0pero se permiten otros tipos de retorno, como para admitir plantillas de expresión. Todos los <=>operadores definidos en el idioma y en la biblioteca estándar devuelven uno de los 5 std::tipos de categorías de comparación mencionados anteriormente .

Para los tipos de idioma, se proporcionan las siguientes <=>comparaciones integradas del mismo tipo. Todos son constexpr , excepto donde se indique lo contrario. Estas comparaciones no pueden invocarse heterogéneamente usando promociones / conversiones escalares.

  • For bool, integral y tipos de puntero, <=>devuelve strong_ordering.
  • Para los tipos de puntero, las diferentes calificaciones de cv y las conversiones derivadas a base pueden invocar una función integrada homogénea <=>, y hay funciones heterogéneas integradas operator<=>(T*, nullptr_t). Solo las comparaciones de punteros al mismo objeto / asignación son expresiones constantes.
  • Para los tipos de coma flotante fundamentales, <=>retornos partial_ordering, y se puede invocar de manera heterogénea ampliando los argumentos a un tipo de coma flotante más grande.
  • Para las enumeraciones, <=>devuelve lo mismo que los tipos subyacentes de la enumeración <=>.
  • Para nullptr_t, <=>vuelve strong_orderingy siempre rinde equal.
  • Para matrices copiables, T[N] <=> T[N]devuelve el mismo tipo que T's <=>y realiza una comparación lexicográfica por elementos. No hay <=>para otras matrices.
  • Por voidallí no está <=>.

Para comprender mejor el funcionamiento interno de este operador, lea el documento original . Esto es justo lo que descubrí usando los motores de búsqueda.

qlp
fuente
1
Como si cpp ya no fuera lo suficientemente complejo. ¿Por qué no simplemente escribir un método de comparación ...
Leandro
66
@Leandro El operador de la nave espacial es ese método de comparación. Además, solo funciona y escribe (o elimina) los otros seis operadores de comparación. Tomaré una función de operador de comparación escrita en seis repeticiones individuales.
anónimo el
Tenga en cuenta que los _equalitytipos murieron: resultó que funciona <=>bien con los cuatro operadores relacionales pero no tan bien con los dos operadores de igualdad (aunque hay algo de azúcar sintáctica intensa para respaldar el caso común donde los quiere a todos).
Davis Herring
12

Esta respuesta se ha vuelto irrelevante desde que la página web referenciada ha cambiado

La página web a la que hace referencia estaba rota. Se estaba editando mucho ese día y las diferentes partes no estaban sincronizadas. El estado cuando lo miraba era:

En la parte superior de la página, enumera los operadores de comparación existentes actualmente (en C ++ 14). No hay <=>ahi.

En la parte inferior de la página, deberían haber enumerado los mismos operadores, pero se burlaron y agregaron esta sugerencia futura.

gccaún no lo sabe <=>(y con -std=c++14, nunca lo sabrá), por lo que cree que lo quiso decir a <= > b. Esto explica el mensaje de error.

Si intenta lo mismo dentro de cinco años, probablemente obtendrá un mejor mensaje de error, algo como <=> not part of C++14.

Stig Hemmer
fuente
1
La página web con enlaces OP es correcta, al igual que la página separada a la que se vincula. Califica al <=>operador con la etiqueta (desde C ++ 20), que le indica en qué versión del estándar debe esperar. El etiquetado de estándares es una convención que sigue cppreference.com. Por supuesto, no tiene un compilador que regresó en una máquina del tiempo para admitirlo, pero cpprefernce le dice (correctamente) qué esperar.
Spencer
Sí, pero ... no es una respuesta. Estás comentando ... o algo así.
qlp
2
Tenía la intención de vincular a la misma página web que la pregunta, pero no lo hice. Creo que respondí las partes de la pregunta que otras respuestas no respondieron. Ignoré la pregunta principal en negrita ya que otros ya habían respondido eso.
Stig Hemmer