it-swarm.com.de

Wie implementiert die Standardbibliothek std :: swap?

Wie ist die Swap-Funktion in der AWL implementiert? Ist es so einfach:

template<typename T> void swap(T& t1, T& t2) {
    T tmp(t1);
    t1=t2;
    t2=tmp;
}

In anderen Beiträgen geht es darum, diese Funktion für Ihre eigene Klasse zu spezialisieren. Warum sollte ich das tun müssen? Warum kann ich die Funktion std::swap Nicht verwenden?

56

Wie ist std::swap implementiert?

Ja, die in der Frage vorgestellte Implementierung ist die klassische C++ 03-Implementierung.

Eine modernere (C++ 11) Implementierung von std::swap sieht aus wie das:

template<typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1); // or T temp(std::move(t1));
    t1 = std::move(t2);
    t2 = std::move(temp);
}

Dies ist eine Verbesserung gegenüber der klassischen C++ 03-Implementierung in Bezug auf die Ressourcenverwaltung, da nicht benötigte Kopien usw. vermieden werden. Dabei handelt es sich um C++ 11 std::swap setzt voraus, dass der Typ T MoveConstructible und = ist MoveAssignable , um die Implementierung und die Verbesserungen zu ermöglichen.

Warum sollte ich eine benutzerdefinierte Implementierung bereitstellen müssen?

Eine benutzerdefinierte Implementierung von swap für einen bestimmten Typ wird normalerweise empfohlen, wenn Ihre Implementierung effizienter oder spezifischer als die Standardversion ist.

Ein klassisches Beispiel hierfür ist, wenn Ihre Klasse eine große Menge an Ressourcen verwaltet, deren Kopieren und anschließendes Löschen teuer wäre. Stattdessen könnte Ihre benutzerdefinierte Implementierung einfach die Handles oder Zeiger austauschen, die zum Ausführen des Austauschs erforderlich sind.

Mit dem Aufkommen von std::move und bewegliche Typen (und haben Ihren Typ als solchen implementiert), ab C++ 11, ein Großteil der ursprünglichen Überlegungen hier beginnt sich aufzulösen; Wenn jedoch ein benutzerdefinierter Swap besser als der Standard-Swap wäre, implementieren Sie ihn.

Generischer Code kann Ihr benutzerdefiniertes swap im Allgemeinen verwenden, wenn er den [~ # ~] adl [~ # ~] -Mechanismus entsprechend verwendet.

57
Niall

Wie ist die Swap-Funktion in der AWL implementiert?

Welche Implementierung? Es ist eine Spezifikation, keine einzelne konkrete Bibliothek. Wenn Sie wie funktioniert die Standardbibliothek meines Compilers meinen, teilen Sie uns entweder mit, welcher Compiler das ist, oder lesen Sie den Code selbst.

Ist es so einfach:

Das ist im Wesentlichen die naive Version vor C++ 11.

Diese nicht spezialisierte Implementierung erzwingt eine Kopie: für T = std::vector<SomethingExpensive> In Ihrem Beispiel lautet der Code wie folgt:

template<typename T> void swap(T& t1, T& t2) {
  T tmp(t1); // duplicate t1, making an expensive copy of each element
  t1=t2;     // discard the original contents of t1,
             // and replace them with an expensive duplicate of t2
  t2=tmp;    // discard the original contents of t2,
             // and replace them with an expensive duplicate of tmp
}            // implicitly destroy the expensive temporary copy of t1

um also zwei Vektoren auszutauschen, haben wir im Wesentlichen drei erstellt. Es wurden drei dynamische Zuordnungen und viele teure Objekte kopiert, und jede dieser Operationen konnte ausgelöst werden, wodurch die Argumente möglicherweise in einem unbestimmten Zustand blieben.

Da dies offensichtlich schrecklich war, wurden Überladungen für teure Container bereitgestellt, und Sie wurden aufgefordert, Überladungen für Ihre eigenen teuren Typen zu schreiben: z. das std::vector Spezialisierung hatte Zugriff auf die Interna des Vektors und konnte zwei Vektoren tauschen, ohne das ganze Kopieren:

template <typename T> void swap(vector<T> &v1, vector<T> &v2) { v1.swap(v2); }
template <typename T> void vector<T>::swap(vector<T>& other) {
  swap(this->size_, other.size_); // cheap integer swap of allocated count
  swap(this->used_, other.used_); // cheap integer swap of used count
  swap(this->data__, other.data_); // cheap pointer swap of data ptr
}

Beachten Sie, dass dies keinerlei teure Kopien oder dynamische (De-) Zuweisungen mit sich bringt und garantiert nicht zu werfen ist.

Der Grund für diese Spezialisierung liegt nun darin, dass vector :: swap Zugriff auf die Interna von vector hat und diese sicher und effizient verschieben kann, ohne sie zu kopieren.

Warum sollte ich das tun müssen?

Pre-C++ 11, aus dem gleichen Grund wie std::vector - um das Tauschen effizient und ausnahmesicher zu machen.

Seit C++ 11 ist dies nicht mehr der Fall - wenn Sie entweder die Verschiebungskonstruktion und -zuweisung bereitstellen oder der Compiler für Sie vernünftige Standardeinstellungen generieren kann.

Der neue generische Tausch:

template <typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1);
    t1 = std::move(t2);
    t2 = std::move(temp);
}

mit Move Construction/Assignment können Sie im Wesentlichen dasselbe Verhalten wie mit der oben beschriebenen benutzerdefinierten Vektorimplementierung erzielen, ohne dass Sie eine benutzerdefinierte Implementierung schreiben müssen.

18
Useless