operator<()
« | 15 May 2022 | »Jahre code ich herum, und dann passieren mir so unglaublich dumme
Anfängerfehler, wie ein falsch implementierter operator<()
.
(C++ Operator overloading)
Und das Schlimme ist, dass man das nicht immer gleich mitbekommt, wenn plötzlich ganz andere Codes anfangen zu bocken.
Es wird also Zeit, das Thema zu “wiederholen” … so lange bis auch ich es richtig mache.
Datentyp als Mapping-Key
Es war einmal eine Datenverwaltung, die einen int
als Schlüssel
nutzte, um Datensätze in einer std::map
leicht auffindbar zu halten.
Dann erkannte man, dass der int
nicht eindeutig genug war, es
sollte ein zweiter angefügt werden und so wurde aus:
1std::map<int, data_t> mapping;
ein
Doch das funktioniert noch nicht, weil int_key
Instanzen vergleichbar
sein müssen, um in einer std::map
benutzt werden zu dürfen.
Dafür schreibt man also eine operator<()
Funktion, die zwei Instanzen
vergleicht, denn (ohne zusätzlich Angaben) nutzt std::map
ein
std::less(x, y)
für die Ordnung innerhalb seiner Elemente und std::less
führt wiederum
return x < y;
aus.
Und die Implementierung kann dann etwa so aussehen:
1bool operator<(int_key const& k1, int_key const& k2) 2{ 3 if(k1.a < k2.a) 4 { 5 // primary value less -> return true 6 return true; 7 } 8 if(k2.a < k1.a) /*OR: if(k1.a > k2.a) */ 9 { 10 // primary value greater -> return false 11 return false; 12 } 13 // primary values are equal -> use second 14 return k1.b < k2.b; 15}
Muss man mehrere Member vergleichen, kann man immer davon ausgehen, dass wenn ein Member kleiner ist als der im Vergleichsobjekt, dann ist das gesamte Objekt bereits “kleiner”. Ist ein Member größer, ist auch das gesamte Objekt größer. Sind beide Member aber gleich, geht man zum nächsten und wiederholt das Spiel, bis am Schluss der letzte Member entscheiden darf, wenn alle seine Vorgänger gleich waren.
So ist alles in Ordnung und eine std::map
kann ihre Daten korrekt
einordnen.
Und was wenn nicht ??
Ich habe neulich folgendes schlampiges Fabrikat abgeliefert:
Und dann brach die Hölle los.
Denn mit der Implementierung werden Größer/Kleiner Aussagen widersprüchlich.
Hier ein Beispiel:
std::map
kann std::less
in beiden Richtungen nutzen, nämlich:
std::less(x, y)
: Istx
kleiner alsy
?std::less(y, x)
: Istx
größer alsy
?
Und wenn beides true
wird, ergeben sich fiese Abfrageschleifen.
So ist das Problem dann auch aufgefallen, nämlich als das Einfügen eines neuen Wertes plötzlich zu einer Endlosschleife wurde.
Dummerweise waren die Zahlen bei Tests zufällig so gestrickt, dass es mir nicht aufgefallen war.
Fazit
Peinlich! Peinlich! Peinlich!
Ja, Tippfehler passieren, aber ich habe schon auch ein Talent solche Kindergartenbeispiele zu versemmeln.
Ein weiterer “meiner” Klassiker ist:
Es ist einfach Schlampigkeit oder mangelnde Konzentration, die so etwas auslösen und genau das sollte einem beim finalen Durchlesen sofort auffallen.
Denn was man nicht vergessen darf: Software steuert heute alles. Und leider sind es dann solche Fehler, die böse Dinge auslösen.
Hoffentlich wird mir diese Peinlichkeit eine Lehre sein, damit ich in Zukunft genau hinsehe, wenn ich wieder mal so einen Blödsinn schreiben sollte.
Und vielen Dank an meinen Kollegen, der meinen Patzer herausgefunden hat, bevor er in produktiver Software breiten Schaden anrichten konnte.