Geschiedenis van de vraag
Voor C++20 implementeerden ontwikkelaars handmatig zes vergelijkingsoperatoren voor sorteere types. Deze boilerplate introduceerde vaak subtiele logische inconsistenties tussen gelijkheid en volgorde. De ruimtevaarderoperator werd geïntroduceerd om deze samen te voegen tot één canonieke operatie.
Het probleem
Hoewel operator<=> de syntaxis vermindert, vertrouwt de compiler op het retourtype om omgekeerde uitdrukkingen zoals b < a van a > b te synthetiseren. Zonder te weten of de volgorde sterk, zwak of gedeeltelijk is, kan de compiler deze herschrijvingen niet veilig genereren.
De oplossing
Het retourtype moet std::strong_ordering, std::weak_ordering of std::partial_ordering zijn (of impliciet converteerbaar). Deze standaardcategorie stelt de compiler in staat om omgekeerde kandidaten en impliciete gelijkheidcontroles te genereren. Het retourneren van auto of aangepaste types schakelt deze synthese uit, waardoor handmatige asymmetrische overloads nodig zijn.
struct Widget { int id; // Correct: stelt genereren van omgekeerde kandidaten mogelijk std::strong_ordering operator<=>(const Widget&) const = default; };
Scenario en Probleem
Het ontwikkelen van een SpatialIndex voor GPU-versnelde geometrie vereiste een BoundingBox-struct met strikte zwakke volgorde voor std::set-invoegingen. De dozen moesten vergelijken met ruwe coördinaatarrays voor ruimtelijke zoekopdrachten.
Oplossing 1: Handmatige operator overload
Het implementeren van twaalf overloads (zes voor BoundingBox, zes voor coördinaatarrays) bood expliciete controle. De lengte liep echter het risico van copy-paste-fouten tussen operator< en operator>, en het onderhouden van consistentie tijdens refactoren bleek vermoeiend.
Oplossing 2: Defaulted spaceship returning std::weak_ordering
Dit genereerde automatisch alle relationele operatoren vanuit één verklaring. Het expliciete retourtype stelde de compiler in staat om omgekeerde vergelijkingen met coördinaatarrays te verwerken. De implementatie garandeerde uitzonderingveiligheid en wiskundige consistentie met nul boilerplate.
Oplossing 3: Auto return
Het gebruik van auto operator<=>(const BoundingBox&) const = default verhinderde de synthese van omgekeerde kandidaten. Het vergelijken van een ruwe array aan de linkerkant met een BoundingBox aan de rechterkant kon niet worden gecompileerd. Deze asymmetrie brak de interface voor ruimtelijke zoekopdrachten.
Beslissing en Resultaat
We kozen voor Oplossing 2 met std::weak_ordering omdat bounding boxes gelijkheid hebben (kruisende dozen vergelijken gelijk) maar niet wiskundige gelijkheid. Dit stelde naadloze integratie met standaardalgoritmen mogelijk terwijl heterogene coördinatenvergelijkingen werden ondersteund.
Waarom synthetiseert de compiler operator== vanuit operator<=>, en wanneer is dit suboptimaal?
De compiler genereert operator== als ((*this <=> other) == 0). Dit zorgt voor consistentie, maar dwingt een volledige elementgewijze vergelijking af, zelfs wanneer gelijkheid wordt gecontroleerd. Expliciet defaulten van operator== staat kortsluitevaluatie toe, waarbij false onmiddellijk wordt geretourneerd bij het eerste verschillende lid.
Hoe breekt het definiëren van operator<=> als een lid in plaats van een verborgen vriend de symmetrie?
Een lid operator<=> staat alleen impliciete conversies toe op de rechteroperand tijdens overload-resolutie. Deze asymmetrie voorkomt dat uitdrukkingen zoals double == MyClass kunnen worden gecompileerd, zelfs als MyClass constructief is uit double. Het gebruik van een verborgen vriend maakt Argument Dependente Lookup (ADL) mogelijk, waardoor beide operandassen impliciet kunnen converteren.
Wat onderscheidt std::compare_three_way van handmatige pointervergelijking?
std::compare_three_way biedt een totale volgorde voor pointers die consistent is over de gehele adresruimte, inclusief std::nullptr_t. Handmatige pointervergelijkingen met relationele operatoren roepen ongedefinieerd gedrag op bij het vergelijken van ongerelateerde objecten. Het gebruik van de standaardfunctie zorgt voor draagbare, goed gedefinieerde semantiek voor pointer sortering.