Namensräume
Varianten
Aktionen

Standardvergleiche (seit C++20)

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Vergleichsoperatorfunktionen können explizit als standardmäßig deklariert werden, um den Compiler anzuweisen, den entsprechenden Standardvergleich für eine Klasse zu generieren.

Inhalt

[edit] Definition

Eine standardmäßig deklarierte Vergleichsoperatorfunktion ist eine nicht-template Vergleichsoperatorfunktion (d.h. <=>, ==, !=, <, >, <= oder >=), die alle folgenden Bedingungen erfüllt:

Eine solche Vergleichsoperatorfunktion wird als standardmäßig deklarierte Vergleichsoperatorfunktion für die Klasse C bezeichnet.

struct X
{
    bool operator==(const X&) const = default; // OK
    bool operator==(const X&) = default;       // Error: the implicit object
                                               //        parameter type is X&
    bool operator==(this X, X) = default;      // OK
};
 
struct Y
{
    friend bool operator==(Y, Y) = default;        // OK
    friend bool operator==(Y, const Y&) = default; // Error: different parameter types
};
 
bool operator==(const Y&, const Y&) = default;     // Error: not a friend of Y

Namenssuchen und Zugriffsprüfungen in der impliziten Definition einer Vergleichsoperatorfunktion werden aus einem Kontext durchgeführt, der ihrem Funktionsrumpf entspricht. Eine als standardmäßig deklarierte Definition einer Vergleichsoperatorfunktion, die in einer Klasse erscheint, muss die erste Deklaration dieser Funktion sein.

[edit] Standardvergleichsreihenfolge

Gegeben sei eine Klasse C; eine Liste von Unterobjekten wird in der folgenden Reihenfolge gebildet:

  • Die direkten Basisklassen-Unterobjekte von C, in deklarierter Reihenfolge.
  • Die nicht-statischen Datenmitglieder von C, in deklarierter Reihenfolge.
  • Wenn ein Mitgliedsunterobjekt vom Array-Typ ist, wird es auf die Sequenz seiner Elemente erweitert, in der Reihenfolge steigender Indizes. Die Erweiterung ist rekursiv: Array-Elemente von Array-Typen werden erneut erweitert, bis kein Unterobjekt vom Array-Typ mehr vorhanden ist.

Für jedes Objekt x vom Typ C gilt in den folgenden Beschreibungen:

struct S {};
 
struct T : S
{
    int arr[2][2];
} t;
 
// The subobject list for “t” consists of the following 5 subobjects in order:
// (S)t → t[0][0] → t[0][1] → t[1][0] → t[1][1]

[edit] Drei-Wege-Vergleich

Ein operator<=> für einen Klassentyp kann mit jedem beliebigen Rückgabetyp als standardmäßig deklariert werden.

[edit] Vergleichskategorietypen

Es gibt drei Vergleichskategorietypen:

Typ  Äquivalente Werte sind..   Nicht vergleichbare Werte sind.. 
std::strong_ordering ununterscheidbar nicht erlaubt
std::weak_ordering unterscheidbar nicht erlaubt
 std::partial_ordering  unterscheidbar erlaubt

[edit] Synthetisierter Drei-Wege-Vergleich

Der synthetisierte Drei-Wege-Vergleich vom Typ T zwischen Glvalues a und b desselben Typs ist wie folgt definiert:

  • Wenn die Überladungsauflösung für a <=> b einen verwendbaren Kandidaten ergibt und explizit zu T mittels static_cast konvertiert werden kann, ist der synthetisierte Vergleich static_cast<T>(a <=> b).
  • Andernfalls, wenn eine der folgenden Bedingungen erfüllt ist, ist der synthetisierte Vergleich nicht definiert:
  • Die Überladungsauflösung für a <=> b findet mindestens einen zulässigen Kandidaten.
  • T ist kein Vergleichskategorietyp.
  • Die Überladungsauflösung für a == b ergibt keinen verwendbaren Kandidaten.
  • Die Überladungsauflösung für a < b ergibt keinen verwendbaren Kandidaten.
a == b ? std::strong_ordering::equal :
a < b  ? std::strong_ordering::less :
         std::strong_ordering::greater
a == b ? std::weak_ordering::equivalent :
a < b  ? std::weak_ordering::less :
         std::weak_ordering::greater
a == b ? std::partial_ordering::equivalent :
a < b  ? std::partial_ordering::less :
b < a  ? std::partial_ordering::greater : 
         std::partial_ordering::unordered

[edit] Platzhalter-Rückgabetyp

Wenn der deklarierte Rückgabetyp einer standardmäßig deklarierten Drei-Wege-Vergleichsoperatorfunktion (operator<=>) für einen Klassentyp C auto ist, wird der Rückgabetyp aus den Rückgabetypen der Drei-Wege-Vergleiche zwischen den entsprechenden Unterobjekten eines Objekts x vom Typ C abgeleitet.

Für jedes Unterobjekt x_i in der (erweiterten) Unterobjektliste für x:

  1. Führen Sie eine Überladungsauflösung für x_i <=> x_i durch. Wenn die Überladungsauflösung keinen verwendbaren Kandidaten ergibt, wird der standardmäßig deklarierte operator<=> als gelöscht definiert.
  2. Bezeichnen Sie die cv-unqualifizierte Version des Typs von x_i <=> x_i als R_i. Wenn R_i kein Vergleichskategorietyp ist, wird der standardmäßig deklarierte operator<=> als gelöscht definiert.

Wenn der standardmäßig deklarierte operator<=> nicht als gelöscht definiert ist, wird sein Rückgabetyp als std::common_comparison_category_t<R_1, R_2, ..., R_n> abgeleitet.

[edit] Nicht-Platzhalter-Rückgabetyp

Wenn der deklarierte Rückgabetyp des standardmäßig deklarierten operator<=> nicht auto ist, darf er keinen Platzhaltertyp enthalten (z.B. decltype(auto)).

Wenn es ein Unterobjekt x_i in der (erweiterten) Unterobjektliste für x gibt, für das der synthetisierte Drei-Wege-Vergleich des deklarierten Rückgabetyps zwischen x_i und x_i nicht definiert ist, wird der standardmäßig deklarierte operator<=> als gelöscht definiert.

[edit] Vergleichsergebnis

Seien x und y die Parameter eines standardmäßig deklarierten operator<=>. Bezeichnen Sie jedes Unterobjekt in der (erweiterten) Unterobjektliste für x und y als x_i bzw. y_i. Der standardmäßige Drei-Wege-Vergleich zwischen x und y wird durch den Vergleich entsprechender Unterobjekte x_i und y_i in aufsteigender i-Reihenfolge durchgeführt.

Sei R der (möglicherweise abgeleitete) Rückgabetyp. Das Vergleichsergebnis zwischen x_i und y_i ist das Ergebnis des synthetisierten Drei-Wege-Vergleichs vom Typ R zwischen x_i und y_i.

  • Während des standardmäßigen Drei-Wege-Vergleichs zwischen x und y, wenn ein unterobjektweiser Vergleich zwischen x_i und y_i ein Ergebnis v_i erzeugt, so dass die kontextuelle Konvertierung von v_i != 0 zu bool true ergibt, ist der Rückgabewert eine Kopie von v_i (die restlichen Unterobjekte werden nicht verglichen).
  • Andernfalls ist der Rückgabewert static_cast<R>(std::strong_ordering::equal).
#include <compare>
#include <iostream>
#include <set>
 
struct Point
{
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
    /* non-comparison functions */
};
 
int main()
{
    Point pt1{1, 1}, pt2{1, 2};
    std::set<Point> s; // OK
    s.insert(pt1);     // OK
 
    // two-way comparison operator functions are not required to be explicitly defined:
    // operator== is implicitly declared (see below)
    // the overload resolutions of other candidates will select rewritten candidates 
    std::cout << std::boolalpha
        << (pt1 == pt2) << ' '  // false
        << (pt1 != pt2) << ' '  // true
        << (pt1 <  pt2) << ' '  // true
        << (pt1 <= pt2) << ' '  // true
        << (pt1 >  pt2) << ' '  // false
        << (pt1 >= pt2) << ' '; // false
}

[edit] Gleichheitsvergleich

[edit] Explizite Deklaration

Ein operator== für einen Klassentyp kann mit dem Rückgabetyp bool als standardmäßig deklariert werden.

Gegeben sei eine Klasse C und ein Objekt x vom Typ C. Wenn es ein Unterobjekt x_i in der (erweiterten) Unterobjektliste für x gibt, für das die Überladungsauflösung für x_i == x_i keinen verwendbaren Kandidaten ergibt, wird der standardmäßig deklarierte operator== als gelöscht definiert.

Seien x und y die Parameter eines standardmäßig deklarierten operator==. Bezeichnen Sie jedes Unterobjekt in der (erweiterten) Unterobjektliste für x und y als x_i bzw. y_i. Der standardmäßige Gleichheitsvergleich zwischen x und y wird durch den Vergleich entsprechender Unterobjekte x_i und y_i in aufsteigender i-Reihenfolge durchgeführt.

Das Vergleichsergebnis zwischen x_i und y_i ist das Ergebnis von x_i == y_i.

  • Während des standardmäßigen Gleichheitsvergleichs zwischen x und y, wenn ein unterobjektweiser Vergleich zwischen x_i und y_i ein Ergebnis v_i erzeugt, so dass die kontextuelle Konvertierung von v_i zu bool false ergibt, ist der Rückgabewert false (die restlichen Unterobjekte werden nicht verglichen).
  • Andernfalls ist der Rückgabewert true.
#include <iostream>
 
struct Point
{
    int x;
    int y;
    bool operator==(const Point&) const = default;
    /* non-comparison functions */
};
 
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== is not defined
}

[edit] Implizite Deklaration

Wenn eine Klasse C keine Mitglieds- oder Freundesfunktion namens operator== explizit deklariert, wird für jeden als standardmäßig deklarierten operator<=> implizit eine Operatorfunktion deklariert. Jede implizit deklarierte operator== hat denselben Zugriff und dieselbe Funktionsdefinition und befindet sich im selben Gültigkeitsbereich der Klasse wie die entsprechende standardmäßig deklarierte operator<=>, mit den folgenden Änderungen:

template<typename T>
struct X
{
    friend constexpr std::partial_ordering operator<=>(X, X)
        requires (sizeof(T) != 1) = default;
    // implicitly declares: friend constexpr bool operator==(X, X)
    //                          requires (sizeof(T) != 1) = default;
 
    [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default;
    // implicitly declares: [[nodiscard]] virtual bool
    //                          operator==(const X&) const = default;
};

[edit] Sekundärvergleich

Eine sekundäre Vergleichsoperatorfunktion (!=, <, >, <= oder >=) für einen Klassentyp kann mit dem Rückgabetyp bool als standardmäßig deklariert werden.

Sei @ einer der fünf sekundären Vergleichsoperatoren. Für jeden standardmäßig deklarierten operator@ mit den Parametern x und y werden bis zu zwei Überladungsauflösungen durchgeführt (ohne den standardmäßig deklarierten operator@ als Kandidaten zu betrachten), um zu bestimmen, ob er als gelöscht definiert wird.

  • Die erste Überladungsauflösung wird für x @ y durchgeführt. Wenn die Überladungsauflösung keinen verwendbaren Kandidaten ergibt oder der ausgewählte Kandidat keine umgeschriebene Kandidatenfunktion ist, wird der standardmäßig deklarierte operator@ als gelöscht definiert. In diesen Fällen erfolgt keine zweite Überladungsauflösung.
  • Die zweite Überladungsauflösung wird für den ausgewählten umgeschriebenen Kandidaten von x @ y durchgeführt. Wenn die Überladungsauflösung keinen verwendbaren Kandidaten ergibt, wird der standardmäßig deklarierte operator@ als gelöscht definiert.

Wenn x @ y nicht implizit in bool konvertiert werden kann, wird der standardmäßig deklarierte operator@ als gelöscht definiert.

Wenn der standardmäßig deklarierte operator@ nicht als gelöscht definiert ist, liefert er x @ y.

struct HasNoRelational {};
 
struct C
{
    friend HasNoRelational operator<=>(const C&, const C&);
    bool operator<(const C&) const = default; // OK, function is defaulted
};

[edit] Schlüsselwörter

default

[edit] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 2539 C++20 der synthetisierte Drei-Wege-Vergleich würde wählen
static_cast, auch wenn die explizite Konvertierung nicht verfügbar ist
wählt nicht
static_cast in diesem Fall
CWG 2546 C++20 der standardmäßig deklarierte sekundäre operator@ war nicht
als gelöscht definiert, wenn die Überladungsauflösung von
x @ y einen nicht verwendbaren umgeschriebenen Kandidaten auswählt
als gelöscht definiert
in diesem Fall
CWG 2547 C++20 es war unklar, ob Vergleichsoperator
Funktionen für Nicht-Klassen standardmäßig deklariert werden können
sie können nicht standardmäßig deklariert werden
CWG 2568 C++20 die implizite Definition von Vergleichsoperator
Funktionen könnte gegen Mitgliedszugriffsregeln verstoßen
Zugriffsprüfungen werden durchgeführt
aus einem Kontext, der äquivalent
zu ihren Funktionsrümpfen ist

[edit] Siehe auch