Ich bin ein großer Fan davon, den Compiler so viel Arbeit wie möglich für Sie erledigen zu lassen. Beim Schreiben einer einfachen Klasse kann der Compiler Ihnen Folgendes kostenlos zur Verfügung stellen:
- Ein Standardkonstruktor (leer)
- Ein Kopierkonstruktor
- Ein Zerstörer
- Ein Zuweisungsoperator (
operator=
)
Es kann Ihnen jedoch keine Vergleichsoperatoren geben - wie operator==
oder operator!=
. Zum Beispiel:
class foo
{
public:
std::string str_;
int n_;
};
foo f1; // Works
foo f2(f1); // Works
foo f3;
f3 = f2; // Works
if (f3 == f2) // Fails
{ }
if (f3 != f2) // Fails
{ }
Gibt es dafür einen guten Grund? Warum sollte ein Vergleich von Mitglied zu Mitglied ein Problem sein? Wenn die Klasse Speicher zuweist, möchten Sie natürlich vorsichtig sein, aber für eine einfache Klasse könnte der Compiler dies sicherlich für Sie tun?
==
, genauso wie es=
unter bestimmten Bedingungen eine Standardautomatikzuweisung ( ) gibt. (Das Argument über Zeiger ist inkonsistent, da die Logik sowohl für=
als==
auch nicht nur für die Sekunde gilt.)Antworten:
Der Compiler würde nicht wissen, ob Sie einen Zeigervergleich oder einen tiefen (internen) Vergleich wünschen.
Es ist sicherer, es einfach nicht zu implementieren und den Programmierer das selbst tun zu lassen. Dann können sie alle Annahmen treffen, die sie mögen.
quelle
operator=
) in der Regel Arbeit im gleichen Kontext wie Vergleichsoperator - das heißt, es wird erwartet , dass nach der Ausführunga = b
,a == b
wahr ist. Es ist auf jeden Fall sinnvoll, dass der Compiler einen Standardwertoperator==
mit derselben Aggregatwertsemantik wie für bereitstelltoperator=
. Ich vermute, dass Paercebal hier tatsächlich insofern richtig ist, alsoperator=
(und Copy Ctor) nur aus Gründen der C-Kompatibilität bereitgestellt werden und sie die Situation nicht verschlimmern wollten.Das Argument, dass der Compiler, wenn er einen Standardkopierkonstruktor bereitstellen kann, einen ähnlichen Standard bereitstellen kann,
operator==()
ist in gewissem Maße sinnvoll. Ich denke, dass der Grund für die Entscheidung, keinen vom Compiler generierten Standard für diesen Operator bereitzustellen, durch die Aussagen von Stroustrup zum Standardkopierkonstruktor in "Das Design und die Entwicklung von C ++" (Abschnitt 11.4.1 - Kontrolle des Kopierens) erraten werden kann. ::Anstelle von "Warum hat C ++ keine Standardeinstellung
operator==()
?" Sollte die Frage lauten: "Warum hat C ++ eine Standardzuweisung und einen Kopierkonstruktor?". Die Antwort lautete, dass diese Elemente von Stroustrup aus Gründen der Abwärtskompatibilität mit C nur ungern aufgenommen wurden (wahrscheinlich die Ursache für die meisten Warzen von C ++, aber wahrscheinlich auch der Hauptgrund für die Popularität von C ++).Für meine eigenen Zwecke enthält das Snippet, das ich für neue Klassen verwende, in meiner IDE Deklarationen für einen privaten Zuweisungsoperator und einen Kopierkonstruktor, sodass ich beim Erstellen einer neuen Klasse keine Standardzuweisungen und Kopiervorgänge erhalte. Ich muss die Deklaration explizit entfernen dieser Operationen aus dem
private:
Abschnitt, wenn ich möchte, dass der Compiler sie für mich generieren kann.quelle
Foo(const Foo&) = delete; // no copy constructor
undFoo& Foo=(const Foo&) = delete; // no assignment operator
struct
, aber ich wünsche mir, dass esclass
sich anders (und vernünftig) verhält. Dabei hätte es auch einen bedeutenderen Unterschied zwischenstruct
undclass
neben dem Standardzugriff gegeben.operator==
. Zu diesem Zeitpunkt ist es nur Syntaxzucker für einen Kesselplattencode. Wenn Sie befürchten, dass der Programmierer auf diese Weise einen Zeiger zwischen Klassenfeldern übersieht, können Sie eine Bedingung hinzufügen, dass er nur für primitive Typen und Objekte funktioniert, die selbst Gleichheitsoperatoren haben. Es gibt jedoch keinen Grund, dies vollständig zu verbieten.Selbst in C ++ 20 generiert der Compiler nicht implizit
operator==
für SieSeit C ++ 20 können Sie jedoch explizit Standardwerte
==
festlegen :Die Standardeinstellung
==
erfolgt in Bezug auf die Mitglieder==
(auf die gleiche Weise wie der Standardkopierkonstruktor in Bezug auf die Mitglieder ). Die neuen Regeln bieten auch die erwartete Beziehung zwischen==
und!=
. Zum Beispiel kann ich mit der obigen Erklärung beides schreiben:Diese spezielle Funktion (Standardeinstellung
operator==
und Symmetrie zwischen==
und!=
) stammt aus einem Vorschlag , der Teil der umfassenderen Sprachfunktion waroperator<=>
.quelle
= default
, was nicht standardmäßig erstellt wird, oder? Es klingt für mich wie Oxymoron ("explizite Standardeinstellung").IMHO gibt es keinen "guten" Grund. Der Grund, warum es so viele Menschen gibt, die dieser Entwurfsentscheidung zustimmen, ist, dass sie nicht gelernt haben, die Kraft der wertebasierten Semantik zu beherrschen. Die Benutzer müssen viele benutzerdefinierte Kopierkonstruktoren, Vergleichsoperatoren und Destruktoren schreiben, da sie in ihrer Implementierung Rohzeiger verwenden.
Bei Verwendung geeigneter intelligenter Zeiger (wie std :: shared_ptr) ist der Standardkopierkonstruktor normalerweise in Ordnung, und die offensichtliche Implementierung des hypothetischen Standardvergleichsoperators wäre ebenso in Ordnung.
quelle
Es wurde geantwortet, dass C ++ nicht == getan hat, weil C nicht getan hat, und hier ist, warum C nur Standard = aber kein == an erster Stelle bereitstellt. C wollte es einfach halten: C implementiert = von memcpy; == kann jedoch aufgrund des Auffüllens nicht von memcmp implementiert werden. Da das Auffüllen nicht initialisiert wird, gibt memcmp an, dass sie unterschiedlich sind, obwohl sie gleich sind. Das gleiche Problem besteht für leere Klassen: memcmp gibt an, dass sie unterschiedlich sind, da die Größe leerer Klassen nicht Null ist. Es kann von oben zu sehen, dass die Umsetzung == ist komplizierter als = in C einige Code Implementierung Beispiel in Bezug auf diese. Ihre Korrektur wird geschätzt, wenn ich falsch liege.
quelle
operator=
- das würde nur für POD-Typen funktionieren, aber C ++ bietet auch einen Standardoperator=
für Nicht-POD-Typen.In diesem Video spricht Alex Stepanov, der Schöpfer von STL, genau diese Frage gegen 13:00 Uhr an. Zusammenfassend argumentiert er, nachdem er die Entwicklung von C ++ beobachtet hat, dass:
Er sagt dann, dass in (ferner) Zukunft == und ! = Implizit generiert werden.
quelle
C ++ 20 bietet eine Möglichkeit, einen Standardvergleichsoperator einfach zu implementieren.
Beispiel von cppreference.com :
quelle
Point
als Beispiel für eine Bestelloperation verwendet wurden, da es keine vernünftige Standardmethode gibt, um zwei Punkte mitx
undy
Koordinaten zu bestellen ...std::set
dass alle Punkte zu machen sind einzigartig, undstd::set
verwendenoperator<
nur.auto
: Können wir in diesem Fall immer davon ausgehen, dass erstd::strong_ordering
von stammt#include <compare>
?std::common_comparison_category_t
, der für diese Klasse zur Standardreihenfolge (std::strong_ordering
) wird.Es ist nicht möglich, Standard zu definieren
==
, aber Sie können Standard definieren,!=
über==
den Sie sich normalerweise selbst definieren sollten. Dazu sollten Sie folgende Dinge tun:Weitere Informationen finden Sie unter http://www.cplusplus.com/reference/std/utility/rel_ops/ .
Wenn Sie definieren
operator<
, können außerdem Operatoren für <=,>,> = bei der Verwendung daraus abgeleitet werdenstd::rel_ops
.Bei der Verwendung sollten Sie jedoch vorsichtig sein,
std::rel_ops
da Vergleichsoperatoren für die Typen abgeleitet werden können, für die Sie nicht erwartet werden.Die bevorzugte Methode, um verwandte Operatoren von einfachen abzuleiten, ist die Verwendung von boost :: -Operatoren .
Der in Boost verwendete Ansatz ist besser, da er die Verwendung des Operators für die Klasse definiert, die Sie nur möchten, nicht für alle Klassen im Gültigkeitsbereich.
Sie können auch "+" aus "+ =", - aus "- =" usw. generieren (siehe vollständige Liste hier )
quelle
!=
nach dem Schreiben des==
Operators keine Standardeinstellung erhalten . Oder ich tat es, aber es fehlte anconst
Ness. Musste es auch selbst schreiben und alles war gut.rel_ops
in C ++ 20 veraltet ist: weil es nicht funktioniert , zumindest nicht überall und schon gar nicht konsequent. Es gibt keinen zuverlässigen Wegsort_decreasing()
zum Kompilieren. Auf der anderen Seite funktioniert Boost.Operators und hat immer funktioniert.C ++ 0x
hateinen Vorschlag für Standardfunktionen. Man könnte also sagen,default operator==;
wir haben gelernt, dass es hilfreich ist, diese Dinge explizit zu machen.quelle
operator==
. Welches ist schade.Konzeptionell ist es nicht einfach, Gleichheit zu definieren. Selbst für POD-Daten könnte man argumentieren, dass selbst wenn die Felder gleich sind, es sich jedoch um ein anderes Objekt (an einer anderen Adresse) handelt, es nicht unbedingt gleich ist. Dies hängt tatsächlich von der Verwendung des Bedieners ab. Leider ist Ihr Compiler nicht psychisch und kann daraus nicht schließen.
Außerdem sind Standardfunktionen hervorragende Möglichkeiten, sich in den Fuß zu schießen. Die von Ihnen beschriebenen Standardeinstellungen dienen im Wesentlichen der Kompatibilität mit POD-Strukturen. Sie verursachen jedoch mehr als genug Chaos, wenn Entwickler sie oder die Semantik der Standardimplementierungen vergessen.
quelle
int
die über copy ctor von einer anderen erstellt wurde, entspricht der, aus der sie erstellt wurde. Für einstruct
von zweiint
Feldern ist es nur logisch, genau auf die gleiche Weise zu arbeiten.+
Operator fast das gleiche Argument vorbringen, da es für Floats nicht assoziativ ist. das heißt(x + y) + z
! =x + (y + z)
aufgrund der Art und Weise, wie FP-Rundungen auftreten. (Dies ist==
wahrscheinlich ein weitaus schlimmeres Problem als weil es für normale numerische Werte gilt.) Sie könnten vorschlagen, einen neuen Additionsoperator hinzuzufügen, der für alle numerischen Typen (sogar int) funktioniert und fast genau der gleiche ist,+
aber assoziativ ( irgendwie). Aber dann würden Sie der Sprache Aufblähung und Verwirrung hinzufügen, ohne wirklich so vielen Menschen zu helfen.Funktionell ist dies möglicherweise kein Problem, aber in Bezug auf die Leistung ist der Standardvergleich von Mitglied zu Mitglied möglicherweise nicht optimaler als die Standardzuweisung / das Kopieren von Mitglied zu Mitglied. Im Gegensatz zur Reihenfolge der Zuweisung wirkt sich die Reihenfolge des Vergleichs auf die Leistung aus, da das erste ungleiche Mitglied impliziert, dass der Rest übersprungen werden kann. Wenn es also einige Mitglieder gibt, die normalerweise gleich sind, möchten Sie sie zuletzt vergleichen, und der Compiler weiß nicht, welche Mitglieder mit größerer Wahrscheinlichkeit gleich sind.
Betrachten Sie dieses Beispiel, in dem
verboseDescription
eine lange Zeichenfolge aus einem relativ kleinen Satz möglicher Wetterbeschreibungen ausgewählt wird.(Natürlich wäre der Compiler berechtigt, die Reihenfolge der Vergleiche zu ignorieren, wenn er erkennt, dass sie keine Nebenwirkungen haben, aber vermutlich würde er seine Warteschlange immer noch aus dem Quellcode entnehmen, wo er selbst keine besseren Informationen hat.)
quelle
Nur damit die Antworten auf diese Frage im Laufe der Zeit vollständig bleiben: Seit C ++ 20 kann sie automatisch mit Befehl generiert werden
auto operator<=>(const foo&) const = default;
Es werden alle Operatoren generiert: == ,! =, <, <=,> Und> =, Einzelheiten finden Sie unter https://en.cppreference.com/w/cpp/language/default_comparisons .
Aufgrund des Aussehens des Bedieners
<=>
wird es als Raumschiffbetreiber bezeichnet. Siehe auch Warum benötigen wir den Raumschiffoperator <=> in C ++? .BEARBEITEN: Auch in C ++ 11 ist ein ziemlich guter Ersatz dafür verfügbar. Ein vollständiges Codebeispiel mit finden
std::tie
Sie unter https://en.cppreference.com/w/cpp/utility/tuple/tiebool operator<(…)
. Der interessante Teil, mit dem gearbeitet wurde,==
ist:std::tie
funktioniert mit allen Vergleichsoperatoren und wird vom Compiler vollständig optimiert.quelle
Ich stimme zu, für POD-Typklassen könnte der Compiler dies für Sie tun. Was Sie jedoch für einfach halten, kann der Compiler falsch machen. Es ist also besser, den Programmierer das tun zu lassen.
Ich hatte einmal einen POD-Fall, in dem zwei der Felder eindeutig waren - ein Vergleich würde also niemals als wahr angesehen werden. Der Vergleich, den ich brauchte, wurde jedoch immer nur mit der Nutzlast verglichen - etwas, das der Compiler niemals verstehen würde oder jemals selbst herausfinden könnte.
Außerdem - das Schreiben dauert nicht lange, oder?!
quelle