std::swap()
wird von vielen Standardcontainern (wie std::list
und std::vector
) beim Sortieren und sogar beim Zuweisen verwendet.
Die Standardimplementierung von swap()
ist jedoch sehr verallgemeinert und für benutzerdefinierte Typen eher ineffizient.
Somit kann Effizienz durch Überladen std::swap()
mit einer benutzerdefinierten typspezifischen Implementierung erzielt werden. Aber wie können Sie es implementieren, damit es von den Standardcontainern verwendet wird?
Antworten:
Der richtige Weg, um Swap zu überladen, besteht darin, ihn in denselben Namespace wie den Swap zu schreiben, damit er über die argumentabhängige Suche (ADL) gefunden werden kann . Eine besonders einfache Sache ist:
quelle
std::sort
, das ADL zum Austauschen von Elementen verwendet, ist nicht konform mit C ++ 03, aber konform mit C ++ 11. Warum -1 eine Antwort, die auf der Tatsache basiert, dass Clients möglicherweise nicht-idiomatischen Code verwenden?Achtung Mozza314
Hier ist eine Simulation der Auswirkungen eines generischen
std::algorithm
Aufrufsstd::swap
, bei der der Benutzer seinen Swap im Namespace std bereitstellt. Da dies ein Experiment ist, verwendet diese Simulationnamespace exp
anstelle vonnamespace std
.Für mich druckt dies aus:
Wenn Ihr Compiler etwas anderes druckt, implementiert er die "Zwei-Phasen-Suche" für Vorlagen nicht korrekt.
Wenn Ihr Compiler konform ist (mit C ++ 98/03/11), gibt er dieselbe Ausgabe aus, die ich zeige. Und in diesem Fall passiert genau das, was Sie befürchten. Und das Einfügen Ihres
swap
Namespacestd
(exp
) hat dies nicht verhindert.Dave und ich sind beide Komiteemitglieder und arbeiten seit einem Jahrzehnt in diesem Bereich des Standards (und sind uns nicht immer einig). Dieses Problem ist jedoch schon lange gelöst, und wir sind uns beide einig, wie es gelöst wurde. Ignorieren Sie Daves Expertenmeinung / Antwort in diesem Bereich auf eigene Gefahr.
Dieses Problem trat nach der Veröffentlichung von C ++ 98 auf. Ab ungefähr 2001 begannen Dave und ich, diesen Bereich zu bearbeiten . Und das ist die moderne Lösung:
Ausgabe ist:
Aktualisieren
Es wurde festgestellt, dass:
funktioniert! Warum also nicht das benutzen?
Stellen Sie sich den Fall vor, dass es sich bei Ihrer
A
Klasse um eine Klassenvorlage handelt:Jetzt funktioniert es nicht mehr. :-(
Sie können also den
swap
Namespace std eingeben und es funktionieren lassen. Aber Sie müssen sich daran zu erinnern setzenswap
inA
‚s - Namensraum für den Fall , wenn Sie eine Vorlage haben:A<T>
. Und da beiden Fälle werden funktionieren , wenn Sie setzenswap
inA
‚s - Namensraum ist es einfach leichter zu merken (und zu lehren andere) zu tun es einfach , dass ein Weg.quelle
template <>
Ihr erstes Beispiel eingebe, erhalte ich eine Ausgabeexp::swap(A, A)
von gcc. Warum also nicht lieber spezialisieren?using std::swap
eine Ausnahme von der Regel "Anweisungen niemals in Header-Dateien einfügen" darstellt? Warum nichtusing std::swap
hineinstecken<algorithm>
? Ich nehme an, es könnte eine winzige Minderheit des Codes von Menschen brechen. Vielleicht die Unterstützung ablehnen und sie irgendwann einsetzen?using std::swap
den Funktionsumfang in Ihren Headern zu beschränken. Ja,swap
ist fast ein Schlüsselwort. Aber nein, es ist kein Schlüsselwort. Exportieren Sie es also am besten nicht in alle Namespaces, bis Sie es wirklich müssen.swap
ist sehr ähnlichoperator==
. Der größte Unterschied besteht darin, dass niemand daran denkt,operator==
mit qualifizierter Namespace-Syntax aufzurufen (das wäre einfach zu hässlich).Es ist Ihnen (nach dem C ++ - Standard) nicht gestattet, std :: swap zu überladen. Sie dürfen jedoch speziell dem std-Namespace Vorlagenspezialisierungen für Ihre eigenen Typen hinzufügen. Z.B
Dann wählen die Verwendungen in den Standardcontainern (und anderswo) Ihre Spezialisierung anstelle der allgemeinen.
Beachten Sie auch, dass die Bereitstellung einer Basisklassenimplementierung von Swap für Ihre abgeleiteten Typen nicht gut genug ist. ZB wenn du hast
Dies funktioniert für Basisklassen. Wenn Sie jedoch versuchen, zwei abgeleitete Objekte auszutauschen, wird die generische Version von std verwendet, da der Vorlagenaustausch genau übereinstimmt (und das Problem vermieden wird, nur die Basisteile Ihrer abgeleiteten Objekte auszutauschen ).
HINWEIS: Ich habe dies aktualisiert, um die falschen Bits aus meiner letzten Antwort zu entfernen. D'oh! (danke puetzk und j_random_hacker für den hinweis)
quelle
Während es richtig ist, dass man dem std :: -Namensraum im Allgemeinen nichts hinzufügen sollte, ist das Hinzufügen von Vorlagenspezialisierungen für benutzerdefinierte Typen ausdrücklich zulässig. Überladen der Funktionen ist nicht. Das ist ein subtiler Unterschied :-)
Eine Spezialisierung von std :: swap würde folgendermaßen aussehen:
Ohne das Template <> -Bit wäre es eher eine undefinierte Überladung als eine zulässige Spezialisierung. @ Wilkas Vorschlag, den Standard-Namespace zu ändern, funktioniert möglicherweise mit Benutzercode (da Koenig Lookup die Version ohne Namespace bevorzugt), dies ist jedoch nicht garantiert und sollte es auch nicht sein (die STL-Implementierung sollte den vollständigen verwenden) -qualifizierter std :: swap).
Es gibt einen Thread zu comp.lang.c ++. Moderiert mit einer langen Diskussion des Themas. Das meiste davon handelt jedoch von einer teilweisen Spezialisierung (was derzeit nicht gut möglich ist).
quelle
vector
Version und es wird verwendet werden .::swap
Ihnen hinzugefügte Überlastung ist spezialisierter als diestd::swap
Überlastungvector
, sodass der Anruf erfasst wird und keine Spezialisierung der letzteren relevant ist. Ich bin mir nicht sicher, wie das ein praktisches Problem ist (aber ich behaupte auch nicht, dass dies eine gute Idee ist!).