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 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

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.

links

social