L'operatore di uguaglianza non viene definito per un'implementazione dell'operatore di astronave personalizzata in C ++ 20


51

Sto riscontrando uno strano comportamento con il nuovo operatore di astronave <=>in C ++ 20. Sto usando Visual Studio 2019 compilatore con /std:c++latest.

Questo codice viene compilato correttamente, come previsto:

#include <compare>

struct X
{
    int Dummy = 0;
    auto operator<=>(const X&) const = default; // Default implementation
};

int main()
{
    X a, b;

    a == b; // OK!

    return 0;
}

Tuttavia, se cambio X in questo:

struct X
{
    int Dummy = 0;
    auto operator<=>(const X& other) const
    {
        return Dummy <=> other.Dummy;
    }
};

Ottengo il seguente errore del compilatore:

error C2676: binary '==': 'X' does not define this operator or a conversion to a type acceptable to the predefined operator

Ho provato anche questo su clang e ho un comportamento simile.

Gradirei qualche spiegazione sul perché l'implementazione predefinita viene generata operator==correttamente, ma quella personalizzata no.

Risposte:


50

Questo è di progettazione.

[class.compare.default] (sottolineatura mia)

3 Se la definizione di classe non dichiara esplicitamente una == funzione di operatore, ma dichiara una funzione di operatore di confronto a tre vie predefinita , una ==funzione di operatore viene dichiarata implicitamente con lo stesso accesso della funzione di operatore di confronto a tre vie. L' ==operatore implicitamente dichiarato per una classe X è un membro inline ed è definito come predefinito nella definizione di X.

Solo un valore predefinito <=>consente l' ==esistenza di un sintetizzato . La logica è che classi come std::vectornon possono usare un default <=>. Inoltre, l'utilizzo di <=>for ==non è il modo più efficiente per confrontare i vettori. <=>deve dare l'ordine esatto, mentre ==può salvare in anticipo confrontando prima le dimensioni.

Se una classe fa qualcosa di speciale nel suo confronto a tre, probabilmente dovrà fare qualcosa di speciale nel suo confronto ==. Pertanto, invece di generare un valore predefinito non sensato, la lingua lo lascia al programmatore.


4
È certamente sensato, a meno che l'astronave non sia buggy. Potenzialmente gravemente inefficiente però ...
Deduplicatore

1
@Deduplicator - La sensibilità è soggettiva. Alcuni direbbero che un'implementazione inefficiente generata silenziosamente non è ragionevole.
StoryTeller - Unslander Monica

45

Durante la standardizzazione di questa funzionalità, è stato deciso che l'uguaglianza e l'ordinamento dovrebbero essere logicamente separati. Pertanto, gli usi del test di uguaglianza ( ==e !=) non invoceranno maioperator<=> . Tuttavia, è stato ancora considerato utile essere in grado di predefinire entrambi con una singola dichiarazione. Quindi, se si è predefiniti operator<=>, è stato deciso che intendevi anche quelli predefinitioperator== (a meno che non sia stato definito in un secondo momento o se lo si fosse definito in precedenza).

Sul motivo per cui questa decisione è stata presa , il ragionamento di base va così. Prendere in considerazione std::string. L'ordinamento di due stringhe è lessicografico; ogni carattere ha il suo valore intero rispetto a ciascun carattere nell'altra stringa. La prima disuguaglianza si traduce nel risultato dell'ordine.

Tuttavia, il test di uguaglianza delle stringhe ha un corto circuito. Se le due stringhe non hanno la stessa lunghezza, non ha senso fare un confronto saggio; non sono uguali. Quindi, se qualcuno sta facendo dei test sull'uguaglianza, non vorrai farlo a lungo se riesci a cortocircuitare.

Si scopre che molti tipi che richiedono un ordinamento definito dall'utente offriranno anche alcuni meccanismi di cortocircuito per i test di uguaglianza. Per impedire alle persone di implementare solo operator<=>e buttare via potenziali prestazioni, forziamo efficacemente tutti a fare entrambe le cose.


5
Questa è una spiegazione molto migliore della risposta accettata
nota

17

Le altre risposte spiegano molto bene perché la lingua è così. Volevo solo aggiungere che nel caso non fosse ovvio, è ovviamente possibile avere un utente fornito operator<=>con un default operator==. Devi solo scrivere esplicitamente l'impostazione predefinita operator==:

struct X
{
    int Dummy = 0;
    auto operator<=>(const X& other) const
    {
        return Dummy <=> other.Dummy;
    }
    bool operator==(const X& other) const = default;
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.