Swap-Operator?
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 mita
, per Copy ConstructorTyp(const Typ&)
.a = b;
– ruft den ZuweisungsoperatorTyp& operator=(const Typ&)
aufb = temp;
– ruft noch einmal den ZuweisungsoperatorTyp& 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 sinda
undb
nicht vertauscht, sondern identisch. Das altea
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
std::swap(a, b);
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.