Espacios de nombres
Variantes
Acciones

Comparaciones por defecto (desde C++20)

De cppreference.com
< cpp‎ | language
 
 
 
 

Provee un medio para solicitarle al compilador que genere operadores relacionales consistentes para una clase.

Contenido

[editar] Sintaxis

return-type class-name::operatorop( const class-name & ) const &(opcional) = default; (1)
friend return-type operatorop( const class-name &, const class-name & ) = default; (2)
friend return-type operatorop( class-name, class-name ) = default; (3)
op - un operador de comparación (<=>, ==, !=, <, >, <=, or >=)
return-type - tipo de retorno de la función. Debe ser:


[editar] Explicación

1)
Declara la comparación predeterminada como una función miembro.
Original:
Declare the defaulted comparison function as a member function.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.
2)
Declara la comparación predeterminada como una función no-miembro.
Original:
Declare the defaulted comparison function as a non-member function.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.
3)
Declara la comparación predeterminada como una función no-miembro. Los argumentos se pasan por valor.
Original:
Declare the defaulted comparison function as a non-member function. Arguments are passed by value.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.


La función de comparación de tres vías (ya sea predeterminada o no) es llamada cuando se vayan a comparar valores usando el operador <, >, <=, >=, o <=> y la resolución de sobrecargas selecciona esta sobrecarga.
Original:
The three-way comparison function (whether defaulted or not) is called whenever values are compared using <, >, <=, >=, or <=> and overload resolution selects this overload.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.
La función de comparación de igualdad (ya sea predeterminada o no) es llamada cuando se vayan a comparar valores usando el operador == o != y la resolución de sobrecargas selecciona esta sobrecarga.
Original:
The equality comparison function (whether defaulted or not) is called whenever values are compared using == or != and overload resolution selects this overload.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.
De manera similar a como sucede con las funciones por defecto de miembros especiales, una comparación por defecto es definida si aplica la regla de una definición o es una expresión constante que requiere evaluación.
Original:
Like defaulted special member functions, a defaulted comparison function is defined if odr-used or needed for constant evaluation.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.

[editar] Comparaciones predeterminadas

[editar] Comparación de tres vías marcada como predeterminada

El operador predeterminado <=> realiza una comparación lexicográfica comparando sucesivamente los subobjetos base (de izquierda a derecha, y primero en profundidad) y luego miembros no estáticos (en orden de declaración) de T para calcular <=>, expandiendo recursivamente los miembros de un contenedor (en orden de subíndice creciente) y deteniéndose cuando se encuentre un resultado de no igualdad, es decir:
Original:
The default operator <=> performs lexicographical comparison by successively comparing the base (left-to-right depth-first) and then non-static member (in declaration order) subobjects of T to compute <=>, recursively expanding array members (in order of increasing subscript), and stopping early when a not-equal result is found, that is:
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.


for /*cada subobjeto base o miembro o de T */
   if (auto cmp = static_cast<R>(compare(lhs.o, rhs.o)); cmp != 0) return cmp;
return static_cast<R>(strong_ordering::equal);


No está especificado si subobjetos base virtuales son comparados más de una vez.
Original:
It is unspecified whether virtual base subobjects are compared more than once.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.
Si el tipo de retorno declarado es auto, entonces el tipo de retorno real es la categoría de comparación común del subobjeto base y miembro, y los elementos miembro del contenedor que se van a comparar (ver std::common_comparison_category). Esto hace que sea más fácil escribir casos en los que el tipo de retorno depende de manera no trivial de los miembros, como por ejemplo:
Original:
If the declared return type is auto, then the actual return type is the common comparison category of the base and member subobject and member array elements to be compared (see std::common_comparison_category). This makes it easier to write cases where the return type non-trivially depends on the members, such as:
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.
template<class T1, class T2>
struct P {
 T1 x1;
 T2 x2;
 friend auto operator<=>(const P&, const P&) = default;
};
Siendo R el tipo de retorno, cada par de subobjetos a, b se compara de la siguiente manera:
Original:
Let R be the return type, each pair of subobjects a, b is compared as follows:
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.


  • Si a <=> b es usable, el resultado de la comparacióńn es static_cast<R>(a <=> b).
    Original:
    If a <=> b is usable, the result of comparison is static_cast<R>(a <=> b).
    The text has been machine-translated via Google Translate.
    You can help to correct and verify the translation. Click here for instructions.
  • De lo contrario, si se realiza la resolución de sobrecarga de operator<=> para a<=>b y se encuentra al menos un candidato, la comparación queda como no definida (operador<=> se define como eliminado).
    Original:
    Otherwise, if overload resolution for operator<=> is performed for a <=> b and at least one candidate is found, the comparison is not defined (operator<=> is defined as deleted).
    The text has been machine-translated via Google Translate.
    You can help to correct and verify the translation. Click here for instructions.
  • De lo contrario, si R no es un tipo de categoría de comparación (ver más abajo), o bien a==b o a<b no es usable, la comparación queda como no definida (operator<=> se define como eliminado).
    Original:
    Otherwise, if R is not a comparison category type (see below), or either a == b or a < b is not usable, the comparison is not defined (operator<=> is defined as deleted).
    The text has been machine-translated via Google Translate.
    You can help to correct and verify the translation. Click here for instructions.
  • De lo contrario, si R es std::strong_ordering el resultado es:
    Original:
    Otherwise, if R is std::strong_ordering, the result is
    The text has been machine-translated via Google Translate.
    You can help to correct and verify the translation. Click here for instructions.
a == b ? R::equal :
a < b  ? R::less :
         R::greater
a == b ? R::equivalent :
a < b  ? R::less :
         R::greater
a == b ? R::equal :
a < b  ? R::less :
b < a  ? R::greater : 
         R::unordered


Según las reglas para cualquier sobrecarga del operator<=>, una sobrecarga predeterminada de <=> también permitirá que el tipo se compare con <, <=, >, y >=.

Si el operator<=> es marcado como predeterminado y el operator== no es declarado, entonces el operator== es marcado implícitamente como predeterminado.
Original:
If operator<=> is defaulted and operator== is not declared at all, then operator== is implicitly defaulted.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.
#include <compare>
struct Point {
  int x;
  int y;
  auto operator<=>(const Point&) const = default;
  // ... otras funciones que no son de comparación ...
};
// el compilador genera todos los seis operadores relacionales
 
#include <iostream>
#include <set>
int main() {
  Point pt1{1, 1}, pt2{1, 2};
  std::set<Point> s; // ok
  s.insert(pt1);     // ok
  std::cout << std::boolalpha
    << (pt1 == pt2) << ' ' // false; operator== es implicitamente predeterminado.
    << (pt1 != pt2) << ' ' // true
    << (pt1 <  pt2) << ' ' // true
    << (pt1 <= pt2) << ' ' // true
    << (pt1 >  pt2) << ' ' // false
    << (pt1 >= pt2) << ' ';// false
}



[editar] Comparación de igualdad marcado como predeterminado

Una clase puede definir el operador operator== como predeterminado, con un valor de retorno bool. Esto generará una comparación de igualdad para cada clase base y sub-objeto miembro, de acuerdo con su orden de declaración. Dos objetos son iguales si los valores de sus miembros y de sus clases base son iguales. La evaluación hará corto circuito si encuentra una diferencia en miembros o en clases base según su orden de declaración.

De acuerdo con las reglas para el operator==, esto también permite evaluar diferencia !=:

struct Point {
  int x;
  int y;
  bool operator==(const Point&) const = default;
  // ... otras funciones que no son de comparación ...
};
// el compilador genera evaluaciones de igualdad por elemento
 
#include <iostream>
int main() {
  Point pt1{3, 5}, pt2{2, 5};
  std::cout << std::boolalpha
    << (pt1 != pt2) << '\n'  // true
    << (pt1 == pt1) << '\n'; // true
  struct [[maybe_unused]] { int x{}, y{}; } p, q;
  // if (p == q) { } // Error: 'operator==' no está definido
}


[editar] Otros operadores de comparación marcados como predeterminados

Cualquiera de los otros cuatro operadores relacionales puede ser marcado explícitamente como predeterminado. Su tipo de retorno deberá ser bool.

Dicho operador se marcará como borrado si falla la resolución de sobrecarga sobre x<=>y (considerando también el operator<=> con orden inverso de parámetros), o si el operator@ no es aplicable al resultado de esa x<=>y. De lo contrario, el operator@ predeterminado llama a x<=>y @ 0 si se seleccionó un operador <=> con el orden original de los parámetros por resolución de sobrecarga, o 0 @ y<=>x de lo contrario:

struct HasNoRelational {};
 
struct C {
  friend HasNoRelational operator<=>(const C&, const C&);
  bool operator<(const C&) = default;                       // ok, la función es borrada
};

De manera similar, el operator!= puede marcarse como predeterminado. Se marca como borrado si falla la resolución de sobrecarga sobre x==y (considerando también el operator== con orden inverso de parámetros) falla, o si el resultado de x==y no tiene el tipo bool. El operator!= predeterminado llama a !(x==y) o !(y==x) según lo seleccionado por la resolución de sobrecarga.

Marcar operadores relacionales como predeterminados puede ser útil para crear funciones cuyas direcciones pueden ser tomadas. Para otros usos, es suficiente proporcionar solo operator<=> y operator==.


[editar] Comparaciones personalizadas y categorías de comparación

Cuando las semánticas predeterminadas no son adecuadas, por ejemplo cuando los miembros deban ser comparados en desorden, o cuando deban usar una comparación que es diferente de su comparación natural, el programador puede definir el operador operator<=> y dejar que el compilador genere los operadores relacionales apropiados. Los tipos de operadores relacionales generados dependen del tipo de retorno del operator<=> definido por el usuario.

Hay tres tipos de retorno disponibles:

Tipo de retorno Operadores Valores equivalentes son Valores no comparables son
std::strong_ordering == != < > <= >= indistinguibles no permitidos
std::weak_ordering == != < > <= >= distinguibles no permitidos
std::partial_ordering == != < > <= >= distinguibles permitidos


[editar] Ordenamiento fuerte

Un ejemplo de un operator<=> personalizado que retorna std::strong_ordering es uno que compara cada miembro de una clase en un orden diferente al predeterminado (ordenar por last_name)

#include <compare>
#include <string>
struct Base {
    std::string zip;
    auto operator<=>(const Base&) const = default;
};
struct TotallyOrdered : Base {
  std::string tax_id;
  std::string first_name;
  std::string last_name;
public:
 // operator<=> personalizado para ordenar por last_name:
 std::strong_ordering operator<=>(const TotallyOrdered& that) const {
   if (auto cmp = (Base&)(*this) <=> (Base&)that; cmp != 0)
       return cmp;
   if (auto cmp = last_name <=> that.last_name; cmp != 0)
       return cmp;
   if (auto cmp = first_name <=> that.first_name; cmp != 0)
       return cmp;
   return tax_id <=> that.tax_id;
 }
  // ... otras funciones que no son de comparación ...
};
// el compilador genera todos los operadores relacionales
 
#include <cassert>
#include <set>
int main() {
  TotallyOrdered to1{"a","b","c","d"}, to2{"a","b","d","c"};
  std::set<TotallyOrdered> s; // ok
  s.insert(to1); // ok
  assert(to2 <= to1); // ok, una sola llamada a <=>
}


Nota: un operador que retorna std::strong_ordering debe comparar cada uno de los miembros, ya que si alguno es omitido la sustituibilidad puede verse comprometida: se hace posible distinguir dos valores que se comparan iguales.
Original:
Note: an operator that returns a std::strong_ordering should compare every member, because if any member is left out, substitutability can be compromised: it becomes possible to distinguish two values that compare equal.
The text has been machine-translated via Google Translate.
You can help to correct and verify the translation. Click here for instructions.

[editar] Ordenamiento débil

Un ejemplo de un operator<=> personalizado que retorna std::weak_ordering es uno que compara cada miembro string de una clase de una manera que no distingue entre mayúsculas y minúsculas; esto es diferente de la comparación predeterminada (requiere un operador personalizado) y es posible distinguir dos cadenas que comparan a igualdad:

class CaseInsensitiveString {
  std::string s;
public:
  std::weak_ordering operator<=>(const CaseInsensitiveString& b) const {
    return case_insensitive_compare(s.c_str(), b.s.c_str());
  }
  std::weak_ordering operator<=>(const char* b) const {
    return case_insensitive_compare(s.c_str(), b);
  }  
  // ... otras funciones que no son de comparación ...
};
// el compilador genera todos los operadores relacionales
 
CaseInsensitiveString cis1, cis2;
std::set<CaseInsensitiveString> s; // ok
s.insert(/*...*/); // ok
if (cis1 <= cis2) { /*...*/ } // ok, realiza una operación de comparación
 
// El compilador también genera todos los ocho operadores relacionales heterogéneos
if (cis1 <= "xyzzy") { /*...*/ } // ok, realiza una operación de comparación
if ("xyzzy" >= cis1) { /*...*/ } // ok, semántica identica

Nota: Este ejemplo demuestra el efecto del operador heterogéneo operator<=>, genera comparaciones heterogéneas en ambas direcciones.

[editar] Ordenamiento parcial

Este ordenamiento es aquel que permite valores no comparables (desordenados), tales como valores NaN en ordenamientos de punto flotante; en este ejemplo, personas que no son familiares:

class PersonInFamilyTree { // ...
public:
  std::partial_ordering operator<=>(const PersonInFamilyTree& that) const {
    if (this->is_the_same_person_as ( that)) return partial_ordering::equivalent;
    if (this->is_transitive_child_of( that)) return partial_ordering::less;
    if (that. is_transitive_child_of(*this)) return partial_ordering::greater;
    return partial_ordering::unordered;
  }
  // ... otras funciones que no son de comparación ...
};
// el compilador genera todos los operadores relacionales
 
PersonInFamilyTree per1, per2;
if (per1 < per2) { /*...*/ } // ok, per2 es un ancestro de per1
else if (per1 > per2) { /*...*/ } // ok, per1 es un ancestro de per2
else if (std::is_eq(per1 <=> per2)) { /*...*/ } // ok, per1 es per2
else { /*...*/ } // per1 y per2 no son familiares
if (per1 <= per2) { /*...*/ } // ok, per2 es per1 o es un ancestro de per1
if (per1 >= per2) { /*...*/ } // ok, per1 es per2 o es un ancestro de per2
if (std::is_neq(per1 <=> per2)) { /*...*/ } // ok, per1 no es per2

[editar] Ver también