Manuelles Swap
Elemente zu vertauschen ist an vielen Stellen eine gefährliche Operation. Zu Zeiten ohne Exceptions und ohne *
Multithreading* war vertauschen noch einfach:
Typ temp = a;
a = b;
b = temp;
Aber was passiert hier genau:
Typ temp = a;
– erzeugt eine neue Instanz und initialisiert sie mit a
, per Copy ConstructorTyp(const Typ&)
.
a = b;
– ruft den Zuweisungsoperator Typ& operator=(const Typ&)
auf
b = temp;
– ruft noch einmal den Zuweisungsoperator Typ& operator=(const Typ&)
auf.
- Weil es eine neue Instanz ist, wird
temp
später per Destruktor weggeräumt.
Problempunkte
Bei diesem Prozedere gibt es vor allem zwei Dinge zu beachten:
- Die Ressourcen, die
Typ
hält, werden diese wegen des Copy C’tors einmal kopiert.
- Schade, wenn dies zum Beispiel ein riesengroßer Speicherblock ist, zu dem vielleicht nur ein Pointer getauscht werden müsste
- Manche Resourcen können nicht kopiert werden: Locks, Mutexe, Filehandles machen Probleme, etc. Dann funktioniert das ganze Vorgehen nicht.
- Was, wenn
b = temp
eine Exception wirft? Der Zuweisungsoperator sollte das nicht, aber wenn man dessen Code nicht unter Kontrolle hat? Dann sind a
und b
nicht vertauscht, sondern identisch. Das alte a
ist verloren.
Der bessere Weg
Daher wird seit jeher die Verwendung der swap
-Funktion aus der Standardbibliothek empfohlen:
using std::swap;
swap(a, b);
oder seit C++11 auch einfach
denn die Namensauflösung der swap
-Implementierung wurde leicht geändert (Details im Buch ;-) oder
bei Stackoverflow).
Edit: Es ist ein Idiom, die swap
-Funktion eine friend
-Memberfunktion zu machen, auch dazu mehr
bei Stackoverflow.
Auch swap
soll – wie der Zuweisungsoperator (und andere Beteiligte) – keine Exceptions werfen. Und da für die swap
-Implementierung empfohlen wird, die Member elementweise wieder mit swap
zu vertauschen, würde statt der Kopie der
Ressourcen * im Falle des großen Datenblocks nur der Pointer ge-swapped * im Falle der nicht-kopierbaren Ressource
diese vertauscht, was gehen sollte.
Daher merke (seit jeher): Eigene Datentypen sollten swap
implementieren.
Neuer Operator :=:
Um die Wichtigkeit von swap
zu unterstreichen, gibt es
den Vorschlag,
swap
als Operator :=:
einzuführen. Das sähe dann so aus:
struct Typ {
Data a;
Feld b;
};
Typ& operator:=:(Typ& lhs, Typ& rhs) noexcept {
lhs.a :=: std::move(rhs.a);
lhs.b :=: std::move(rhs.b);
return lhs;
}
Typ& operator:=:(Typ& lhs, Typ&& rhs) noexcept {
return rhs;
}
Statt nacheinander die Elemente mit swap
zu vertauschen verwendet man den neuen :=:
-Operator. Beziehungsweise
die &&
-Variante, die für das swappen mit der rhs
-Instanz da ist, die ohnehin bald weggeworfen würde gestaltet sich
die Implementierung besonders leicht: Nimm als lhs
einfach rhs
.
Dadurch ist hervorgehoben, dass es sich um eine sehr spezielle Funktion handelt, und kommt somit namenstechnisch
dem operator=
näher. Der Benutzer wird eher darauf gestoßen, dass er sie implementieren sollte.
Es gibt nich weitere
kleine Ergänzungen
für :=:
, zum Beispiel für die eingebauten Typen.
Glaskugel
Hauptsächlich handelt es sich um eine Sytnax-Erweiterung mit nur wenig funktionalen Ergänzungen. Meine Vorraussage ist,
dass dieses Feature es nicht in den Standard schafft. Wenn es nur um Syntax geht, war das Kommitee eher zurückhaltend.