Default comparisons (since C++20)
Vergleichsoperatorfunktionen können explizit als Standard festgelegt werden, um den Compiler zu veranlassen, die entsprechende Standardvergleichsfunktion für eine Klasse zu generieren.
Inhaltsverzeichnis |
Definition
Eine
defaulted comparison operator function
ist eine Nicht-Template-Vergleichsoperatorfunktion (d.h.
<=>
,
==
,
!=
,
<
,
>
,
<=
, oder
>=
), die alle folgenden Bedingungen erfüllt:
-
Es handelt sich um eine
nicht-statische Elementfunktion
oder einen
Friend
einer Klasse
C. -
Sie ist
als defaulted definiert
in
Coder in einem Kontext, in demCvollständig ist. -
Sie hat zwei Parameter vom Typ
const
C
&
oder zwei Parameter vom Typ
C, wobei der implizite Objektparameter (falls vorhanden) als erster Parameter betrachtet wird.
Eine solche Vergleichsoperatorfunktion wird als
standardmäßige Vergleichsoperatorfunktion für Klasse
C
bezeichnet.
struct X { bool operator==(const X&) const = default; // OK bool operator==(const X&) = default; // Fehler: Der implizite Objekt- // parameter-Typ ist X& bool operator==(this X, X) = default; // OK }; struct Y { friend bool operator==(Y, Y) = default; // OK friend bool operator==(Y, const Y&) = default; // Fehler: Unterschiedliche Parametertypen }; bool operator==(const Y&, const Y&) = default; // Fehler: Kein Freund von Y
Namensauflösungen und Zugriffsprüfungen in der impliziten Definition einer Vergleichsoperatorfunktion werden aus einem Kontext durchgeführt, der ihrem Funktionsrumpf entspricht. Eine Definition einer Vergleichsoperatorfunktion als defaulted, die in einer Klasse erscheint, muss die erste Deklaration dieser Funktion sein.
Standardmäßige Vergleichsreihenfolge
Gegeben eine Klasse
C
, wird eine Unterobjektliste durch die folgenden Unterobjekte in dieser Reihenfolge gebildet:
-
Die direkten Basisklassen-Subobjekte von
C, in Deklarationsreihenfolge. -
Die nicht-statischen
Datenelemente
von
C, in Deklarationsreihenfolge.
-
- Wenn ein beliebiges Mitgliedssubobjekt vom Array-Typ ist, wird es zur Sequenz seiner Elemente erweitert, in der Reihenfolge steigender Indizes. Die Erweiterung ist rekursiv: Array-Elemente von Array-Typen werden erneut erweitert, bis kein Subobjekt mehr vom Array-Typ vorhanden ist.
Für jedes Objekt
x
vom Typ
C
gelten in den folgenden Beschreibungen:
- Sei n die Anzahl der Unterobjekte in der (erweiterten) Unterobjektliste für x .
- Sei x_i das i -te Unterobjekt in der (erweiterten) Unterobjektliste für x , wobei x_i durch eine Sequenz von abgeleiteten-zu-Basis-Konvertierungen , Klassen-Member-Zugriffsausdrücken und Array-Index-Ausdrücken gebildet wird, angewendet auf x .
struct S {}; struct T : S { int arr[2][2]; } t; // Die Unterobjektliste für "t" besteht aus den folgenden 5 Unterobjekten in dieser Reihenfolge: // (S)t → t[0][0] → t[0][1] → t[1][0] → t[1][1]
Dreifachvergleich
Ein operator <=> für einen Klassentyp kann als standardmäßig mit jedem Rückgabetyp definiert werden.
Vergleichskategorien-Typen
Es gibt drei Vergleichskategorien-Typen:
| Typ | Äquivalente Werte sind.. | Unvergleichbare Werte sind.. |
|---|---|---|
| std::strong_ordering | ununterscheidbar | nicht erlaubt |
| std::weak_ordering | unterscheidbar | nicht erlaubt |
| std::partial_ordering | unterscheidbar | erlaubt |
Synthetisierter Drei-Wege-Vergleich
Der
synthetisierte Drei-Wege-Vergleich
vom Typ
T
zwischen Glvalues
a
und
b
desselben Typs wird wie folgt definiert:
-
Wenn die Überladungsauflösung für
a
<=>
b
einen verwendbaren Kandidaten ergibt und explizit zu
Tmittelsstatic_castkonvertiert 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 brauchbaren Kandidaten.
-
Tist kein Vergleichskategorientyp. - Die Überladungsauflösung für a == b ergibt keinen verwendbaren Kandidaten.
- Die Überladungsauflösung für a < b ergibt keinen verwendbaren Kandidaten.
-
Andernfalls, falls
Tein std::strong_ordering ist, ist der synthetisierte Vergleich
a == b ? std::strong_ordering::equal : a < b ? std::strong_ordering::less : std::strong_ordering::greater
-
Andernfalls, falls
Tvom Typ std::weak_ordering ist, ist der synthetisierte Vergleich
a == b ? std::weak_ordering::equivalent : a < b ? std::weak_ordering::less : std::weak_ordering::greater
-
Andernfalls (
Tist std::partial_ordering ), ist der synthetisierte Vergleich
a == b ? std::partial_ordering::equivalent : a < b ? std::partial_ordering::less : b < a ? std::partial_ordering::greater : std::partial_ordering::unordered
Platzhalter-Rückgabetyp
Wenn der deklarierte Rückgabetyp einer defaulted Drei-Wege-Vergleichsoperator-Funktion (
operator
<=>
) für einen Klassentyp
C
als
auto
angegeben 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 :
- Führe Überladungsauflösung für x_i <=> x_i durch. Wenn die Überladungsauflösung keinen verwendbaren Kandidaten ergibt, wird der standardmäßige operator <=> als gelöscht definiert.
-
Bezeichne die cv-unqualifizierte Version des Typs von
x_i
<=>
x_i
als
R_i. WennR_ikein Vergleichskategorientyp ist, wird der standardmäßige operator <=> als gelöscht definiert.
Wenn der standardmäßig definierte operator <=> nicht als gelöscht definiert ist, wird sein Rückgabetyp als std:: common_comparison_category_t < R_1, R_2, ..., R_n > abgeleitet.
Nicht-Platzhalter-Rückgabetyp
Wenn der deklarierte Rückgabetyp des standardmäßigen 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, sodass der synthetisierte Drei-Wege-Vergleich des deklarierten Rückgabetyps zwischen x_i und x_i nicht definiert ist, wird der standardmäßig implementierte operator <=> als gelöscht definiert.
Vergleichsergebnis
Seien x und y die Parameter eines standardmäßigen operator <=> , bezeichnet 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 durchgeführt, indem entsprechende Unterobjekte x_i und y_i in aufsteigender i -Reihenfolge verglichen werden.
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 , falls ein teilobjektweiser Vergleich zwischen x_i und y_i ein Ergebnis v_i erzeugt, bei dem die kontextuelle Konvertierung von v_i ! = 0 zu bool true ergibt, ist der Rückgabewert eine Kopie von v_i (die verbleibenden Teilobjekte 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 // Zwei-Wege-Vergleichsoperatoren müssen nicht explizit definiert werden: // operator== wird implizit deklariert (siehe unten) // die Überladungsauflösung anderer Kandidaten wird umgeschriebene Kandidaten auswählen std::cout << std::boolalpha << (pt1 == pt2) << ' ' // false << (pt1 != pt2) << ' ' // true << (pt1 < pt2) << ' ' // true << (pt1 <= pt2) << ' ' // true << (pt1 > pt2) << ' ' // false << (pt1 >= pt2) << ' '; // false }
Gleichheitsvergleich
Explizite Deklaration
Ein operator == für einen Klassentyp kann als standardmäßig mit Rückgabetyp bool definiert werden.
Gegeben eine Klasse
C
und ein Objekt
x
vom Typ
C
, falls es ein Unterobjekt
x_i
in der (erweiterten) Unterobjektliste für
x
gibt, sodass die Überladungsauflösung für
x_i
==
x_i
nicht zu einem verwendbaren Kandidaten führt, wird der standardmäßig implementierte
operator
==
als gelöscht definiert.
Seien x und y die Parameter eines standardmäßigen operator == , bezeichnet 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 durchgeführt, indem entsprechende Unterobjekte x_i und y_i in aufsteigender i -Reihenfolge verglichen werden.
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 , falls ein subobjektweiser Vergleich zwischen x_i und y_i ein Ergebnis v_i erzeugt, sodass die kontextuelle Konvertierung von v_i zu bool false ergibt, ist der Rückgabewert false (die verbleibenden Subobjekte 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 }
Implizite Deklaration
Wenn eine Klasse
C
keinen Member oder Friend namens
operator
==
explizit deklariert, wird für jeden als default definierten
operator
<=>
implizit eine
==
-Operatorfunktion deklariert. Jeder implizit deklarierte
operator
==
hat denselben Zugriff und dieselbe
Funktionsdefinition
sowie denselben
Klassenbereich
wie der jeweilige default-definierte
operator
<=>
, mit folgenden Änderungen:
- Der Deklarator-Identifikator wird ersetzt durch operator == .
- Der Rückgabetyp wird ersetzt durch bool .
template<typename T> struct X { friend constexpr std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default; // deklariert implizit: friend constexpr bool operator==(X, X) // requires (sizeof(T) != 1) = default; [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default; // deklariert implizit: [[nodiscard]] virtual bool // operator==(const X&) const = default; };
Sekundärer Vergleich
Eine sekundäre Vergleichsoperatorfunktion (
!=
,
<
,
>
,
<=
, oder
>=
) für einen Klassentyp kann als standarddefiniert mit Rückgabetyp
bool
definiert werden.
Sei
@
einer der fünf sekundären Vergleichsoperatoren. Für jeden standardmäßig bereitgestellten
operator@
mit Parametern
x
und
y
werden bis zu zwei Überladungsauflösungen durchgeführt (ohne den standardmäßigen
operator@
als Kandidat zu betrachten), um zu bestimmen, ob er als gelöscht definiert ist.
- Die erste Überladungsauflösung wird für x @ y durchgeführt. Wenn die Überladungsauflösung nicht zu einem verwendbaren Kandidaten führt oder der ausgewählte Kandidat kein umgeschriebener Kandidat ist, wird der standardmäßige operator@ als gelöscht definiert. In diesen Fällen gibt es 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 nicht zu einem verwendbaren Kandidaten führt, wird der standardmäßige operator@ als gelöscht definiert.
Wenn x @ y nicht implizit zu bool konvertiert werden kann, ist der standardmäßige operator@ als gelöscht definiert.
Wenn der standardmäßig bereitgestellte operator@ nicht als gelöscht definiert ist, ergibt er x @ y .
struct HasNoRelational {}; struct C { friend HasNoRelational operator<=>(const C&, const C&); bool operator<(const C&) const = default; // OK, Funktion ist standardmäßig implementiert };
Schlüsselwörter
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 2539 | C++20 |
der synthetisierte Drei-Wege-Vergleich würde
static_cast wählen, selbst wenn die explizite Konvertierung nicht verfügbar ist |
wählt in diesem Fall nicht
static_cast |
| CWG 2546 | C++20 |
der standardmäßige sekundäre
operator@
wurde nicht
als gelöscht definiert, wenn die Überladungsauflösung von x @ y einen nicht verwendbaren umgeschriebenen Kandidaten auswählt |
in diesem Fall als gelöscht
definiert |
| CWG 2547 | C++20 |
es war unklar, ob Vergleichsoperator-
funktionen für Nicht-Klassen standardmäßig definiert werden können |
sie können nicht standardmäßig definiert werden |
| CWG 2568 | C++20 |
die implizite Definition von Vergleichsoperator-
funktionen könnte Mitgliedszugriffsregeln verletzen |
Zugriffsprüfungen werden
aus einem Kontext durchgeführt, der ihren Funktionsrumpf entspricht |
Siehe auch
- Überladenauflösung in einem Aufruf eines überladenen Operators
- Eingebauter Drei-Wege-Vergleichsoperator
- Operatorüberladung für Vergleichsoperatoren