Historia de la pregunta
Antes de C++20, los desarrolladores implementaban manualmente seis operadores de comparación para tipos ordenables. Este código repetitivo a menudo introducía inconsistencias lógicas sutiles entre relaciones de igualdad y de orden. Se introdujo el operador nave espacial para consolidar estas en una única operación canónica.
El problema
Mientras que operator<=> reduce la sintaxis, el compilador depende de su tipo de retorno para sintetizar expresiones inversas como b < a a partir de a > b. Sin saber si el orden es fuerte, débil o parcial, el compilador no puede generar de manera segura estas reescrituras.
La solución
El tipo de retorno debe ser std::strong_ordering, std::weak_ordering o std::partial_ordering (o implícitamente convertible). Esta categoría estándar permite al compilador generar candidatos invertidos y verificaciones de igualdad implícitas. Retornar auto o tipos personalizados desactiva esta síntesis, requiriendo sobrecargas asimétricas manuales.
struct Widget { int id; // Correcto: permite la generación de candidatos invertidos std::strong_ordering operator<=>(const Widget&) const = default; };
Escenario y problema
Desarrollar un SpatialIndex para geometría acelerada por GPU requería una estructura BoundingBox con ordenamiento débil estricto para la inserción en std::set. Las cajas necesitaban compararse con arreglos de coordenadas crudas para consultas espaciales.
Solución 1: Sobrecarga de operador manual
Implementar doce sobrecargas (seis para BoundingBox, seis para arreglos de coordenadas) proporcionó control explícito. Sin embargo, la verbosidad arriesgaba errores de copiar-pegar entre operator< y operator>, y mantener la consistencia durante las refactorizaciones resultó tedioso.
Solución 2: Nave espacial predeterminada devolviendo std::weak_ordering
Esto generó todos los operadores relacionales automáticamente a partir de una sola declaración. El tipo de retorno explícito permitió al compilador manejar comparaciones inversas contra arreglos de coordenadas. La implementación garantizaba seguridad ante excepciones y consistencia matemática con cero código repetitivo.
Solución 3: Retorno auto
Usar auto operator<=>(const BoundingBox&) const = default impidió la síntesis de candidatos invertidos. Comparar un arreglo crudo a la izquierda con un BoundingBox a la derecha falló en compilar. Esta asimetría rompió la interfaz de consulta espacial.
Decisión y resultado
Elegimos la Solución 2 con std::weak_ordering porque las cajas delimitadoras tienen equivalencia (las cajas que se intersectan se comparan como iguales) pero no igualdad matemática. Esto permitió una integración fluida con algoritmos estándar mientras se admitían comparaciones de coordenadas heterogéneas.
¿Por qué el compilador sintetiza operator== a partir de operator<=>, y cuándo es subóptimo?
El compilador genera operator== como ((*this <=> other) == 0). Esto proporciona consistencia, pero fuerza una comparación completa elemento por elemento incluso al verificar la igualdad. Definir explícitamente operator== permite evaluación de cortocircuito, devolviendo false inmediatamente ante el primer miembro diferente.
¿Cómo romper la simetría al definir operator<=> como miembro en lugar de como amigo oculto?
Un operator<=> miembro solo permite conversiones implícitas en el operando de la derecha durante la resolución de sobrecargas. Esta asimetría impide que expresiones como double == MyClass se compilen incluso si MyClass es construible a partir de double. Utilizar un amigo oculto permite la Búsqueda Dependiente de Argumentos (ADL), permitiendo que ambos operandos se conviertan implícitamente.
¿Qué distingue a std::compare_three_way de la comparación manual de punteros?
std::compare_three_way proporciona un orden total para punteros que es consistente a través de toda el espacio de direcciones, incluyendo std::nullptr_t. Las comparaciones manuales de punteros usando operadores relacionales invocan comportamiento indefinido al comparar objetos no relacionados. Usar el objeto de función estándar asegura semánticas portables y bien definidas para la ordenación de punteros.