Warum muss ich operator==in POD-Typen überladen?

Warum muss ich operator==in POD-Typen überladen?

Es gibt keinen technischen Grund. Umständlich könnte man sagen, das liege daran, dass C es nicht erlaubt, zwei Strukturen mit == zu vergleichen , und das ist ein guter Grund; dass sich das Verhalten ändert, wenn Sie zu C++ wechseln, ist nicht offensichtlich. (Vermutlich liegt der Grund dafür, dass C das nicht unterstützt, darin, dass der feldweise Vergleich für einige funktionieren könnte Strukturen, aber definitiv nicht alle.)

Und nur aus C++-Sicht, was ist, wenn Sie ein privates Feld haben? Ein Standard == macht dieses Feld technisch verfügbar (indirekt, aber immer noch). Der Compiler würde also nur einen operator== generieren wenn es keine privaten oder geschützten Datenelemente gibt?

Außerdem gibt es Klassen, die keine vernünftige Definition von Gleichheit haben (leere Klassen, Klassen, die den Zustand nicht modellieren, sondern zwischenspeichern usw.), oder für die die standardmäßige Gleichheitsprüfung extrem verwirrend sein könnte (Klassen, die Zeiger umschließen).

Und dann gibt es Erbschaft. Entscheidung, was für operator== zu tun ist in einer Situation der Vererbung ist kompliziert, und es wäre leicht für den Compiler, die falsche Entscheidung zu treffen. (Wenn dies beispielsweise C++ getan hätte, würden wir wahrscheinlich Fragen dazu bekommen, warum == immer erfolgreich, wenn Sie die Gleichheit zwischen zwei Objekten testen, die beide Nachkommen einer abstrakten Basisklasse sind und mit einer Referenz darauf verwendet werden.)

Im Grunde ist es ein heikles Problem, und es ist sicherer für den Compiler, sich da rauszuhalten, selbst wenn man bedenkt, dass man alles überschreiben könnte, was der Compiler entschieden hat.


Die Frage, warum Sie operator== angeben müssen ist nicht dasselbe wie die Frage, warum Sie eine Vergleichsfunktion bereitstellen müssen .

In Bezug auf letzteres liegt der Grund dafür, dass Sie die Vergleichslogik bereitstellen müssen, darin, dass die elementweise Gleichheit selten angemessen ist. Betrachten Sie zum Beispiel eine POD-Struktur mit einem Array von char da drin. Wenn es verwendet wird, um eine nullterminierte Zeichenfolge zu halten, können zwei solcher Strukturen auf der binären Ebene ungleich sein (aufgrund willkürlicher Inhalte nach den Null-Bytes in den Zeichenfolgen), aber dennoch logisch äquivalent sein.

Darüber hinaus gibt es alle Komplikationen auf C ++ - Ebene, die hier in anderen Antworten erwähnt werden, z. die besonders heikle Frage der polymorphen Gleichheit (Sie wollen wirklich nicht, dass der Compiler wählt!).

Im Wesentlichen gibt es also einfach keine gute Standardauswahl, also liegt die Wahl bei Ihnen.

In Bezug auf die erste Frage, die Sie wörtlich gestellt haben, warum müssen Sie operator== angeben ?

Wenn Sie operator< definieren und operator== , dann die Operatordefinitionen im Namespace std::rel_ops kann den Rest für Sie ausfüllen. Vermutlich der Grund warum operator== benötigt wird, ist, dass es unnötig ineffizient wäre, es im Sinne von operator< zu implementieren (dann sind zwei erforderlich Vergleiche). Die Wahl dieser beiden Operatoren als Basis ist jedoch völlig verwirrend, da sie den Benutzercode ausführlich und kompliziert macht und in einigen Fällen viel weniger effizient als möglich!

Die IMHO beste Basis für Vergleichsoperatoren ist stattdessen der dreiwertige compare Funktion, wie z. B. std::string::compare .

Gegeben sei eine Mitgliedsfunktionsvariante comparedTo , können Sie dann eine Curiously Recurring Template Pattern-Klasse wie die folgende verwenden, um den vollständigen Satz von Operatoren bereitzustellen:

template< class Derived >
class ComparisionOps
{
public:
    friend int compare( Derived const a, Derived const& b )
    {
        return a.comparedTo( b );
    }

    friend bool operator<( Derived const a, Derived const b )
    {
        return (compare( a, b ) < 0);
    }

    friend bool operator<=( Derived const a, Derived const b )
    {
        return (compare( a, b ) <= 0);
    }

    friend bool operator==( Derived const a, Derived const b )
    {
        return (compare( a, b ) == 0);
    }

    friend bool operator>=( Derived const a, Derived const b )
    {
        return (compare( a, b ) >= 0);
    }

    friend bool operator>( Derived const a, Derived const b )
    {
        return (compare( a, b ) > 0);
    }

    friend bool operator!=( Derived const a, Derived const b )
    {
        return (compare( a, b ) != 0);
    }
};

wobei compare ist eine überladene Funktion, z. so:

template< class Type >
inline bool lt( Type const& a, Type const& b )
{
    return std::less<Type>()( a, b );
}

template< class Type >
inline bool eq( Type const& a, Type const& b )
{
    return std::equal_to<Type>()( a, b );
}

template< class Type >
inline int compare( Type const& a, Type const b )
{
    return (lt( a, b )? -1 : eq( a, b )? 0 : +1);
}

template< class Char >
inline int compare( basic_string<Char> const& a, basic_string<Char> const& b )
{
    return a.compare( b );
}

template< class Char >
inline int compareCStrings( Char const a[], Char const b[] )
{
    typedef char_traits<Char>   Traits;

    Size const  aLen    = Traits::length( a );
    Size const  bLen    = Traits::length( b );

    // Since there can be negative Char values, cannot rely on comparision stopping
    // at zero termination (this can probably be much optimized at assembly level):
    int const way = Traits::compare( a, b, min( aLen, bLen ) );
    return (way == 0? compare( aLen, bLen ) : way);
}

inline int compare( char const a[], char const b[] )
{
    return compareCStrings( a, b );
}

inline int compare( wchar_t const a[], wchar_t const b[] )
{
    return compareCStrings( a, b );
}

Nun, das ist die Maschinerie . Wie sieht es aus, es auf Ihre Klasse anzuwenden …

struct Vec3
{
    float x, y, z;
};

?

Nun, es ist ziemlich einfach:

struct Vec3
    : public ComparisionOps<Vec3>
{
    float x, y, z;

    int comparedTo( Vec3 const& other ) const
    {
        if( int c = compare( x, other.x ) ) { return c; }
        if( int c = compare( y, other.y ) ) { return c; }
        if( int c = compare( z, other.z ) ) { return c; }
        return 0;   // Equal.
    }
};

Haftungsausschluss:nicht sehr getesteter Code… :-)