Sonderfall: Swapping den Wert von zwei Variablen
(. Für die allgemeine Lösung, siehe unten)
zwei in C-Werte der Variablen vertauschen ++, sollten Sie immer swap
verwenden:
using std::swap;
swap(x, y); // Do NOT say: std::swap(x, y) -- Read about Koenig lookup!
stören sie nicht wie es es tun; es wird es tun sehr schnell. Die Implementierung der C++ - Standardbibliothek wird ihr Bestes tun, um dies zu einem Einzelbefehl zu optimieren, wenn der Prozessor dies unterstützt (aber der Standard teilt der Implementierung dies nicht mit). Für Register-Only-Variablen gibt es zum Beispiel die x86-Anweisung xchg
, die es so schnell wie möglich macht. Versuchen Sie nicht, es mit einigen "drei XOR-Operationen" zu optimieren, es wird nicht schneller sein. Wenn Sie Pech haben, wird es nicht zu etwas wie xchg
optimiert werden.
Die generische Operation swap
in C++ 03 führt eine temporäre Variable ein und führt drei Kopienkonstruktionen aus. In C++ 11 gibt es move-semantics und die Objekte werden eher verschoben als kopiert.Für Ihre eigene Typen, lassen Sie uns einige Datenstruktur sagen, die nur einen Zeiger auf die tatsächlichen Daten enthält, sollten Sie dieses Verfahren optimieren, um es in konstanter Zeit zu machen ausführen:
In C++ 03, Sie können entweder std::swap
spezialisieren oder Ihre eigene swap
Funktion in Ihrem Namespace (see the two top answers on this question) implementieren, um das Swapping zu optimieren: Tauschen Sie einfach jedes Mitglied in Ihrer Klasse aus, um ihre Daten zu tauschen. Für das Datenstrukturbeispiel, das nur einen Zeiger enthält, vertauschen Sie einfach die Zeiger.
In C++ 11 gibt es die neue bewegt Semantik, die Sie Bewegung der Daten von einem auf ein anderes Objekt implementieren lassen, die in einem sehr ähnlichen Verhalten führen. (Verschiebungssemantik wurde für allgemeinere Probleme wie das Tauschen von zwei Objekten eingeführt: Wenn ein Objekt nicht mehr benötigt wird, aber ein anderes "Kopie" des ersten sein muss, kann es einfach verschoben werden.) Lesen Sie über Verschiebungssemantik und Konstruktor verschieben für Details.
Für sowohl C++ 03 und C++ 11 gibt es eine alternative Art und Weise: Sie können implementieren implizit gemeinsam genutzte Daten und copy-on-write für schwere Klassen wie Datenstrukturen. Im obigen Beispiel, in dem Ihre Datenstruktur einen Zeiger auf die tatsächlichen Daten enthält, implementieren Sie die Referenzzählung. Wenn Sie die Datenstruktur kopieren, erhöhen Sie einfach den Referenzzähler um eins. Stellen Sie beim Ändern der Daten sicher, dass es nicht freigegeben ist (ref count = 1), andernfalls "trennen" Sie es, indem Sie es nur dann kopieren. Dies führt zu einer konstanten Kopier- und Tauschoperation.
Allgemeiner Fall: Mehrere beliebige Ausdrücke
Für andere Aussagen, die nicht Eingabe/Ausgabe-abhängig sind, wie (a, b) = (x, y)
, sie nur schreiben „wie sie ist“, und es wird laufen zumindest perfekt pipelineed, da sie keine Abhängigkeit haben:
a = x;
b = y;
Wenn Sie sind Eingabe/Ausgabe abhängig, wie Ihr Beispiel in der Bearbeitung, können Sie es aufteilen und provisorisch einführen. Du wirst dir keinen Gefallen tun, wenn du versuchst, das mit einigen ausgefallenen Ausdruckstricks wie xor-ing zu lösen. Der Compiler kennt eine Menge Tricks für Assembler (wie xchg
), man kennt nur Tricks, um solche in reinem C++ (wie xor) auszudrücken.
In C++ 11 gibt es std::tuple
und std::tie
, so dass Sie mehrere Ausdrücke ohne Einführung von Provisorien zuweisen können (sie werden hinter den Kulissen eingeführt, um die im Tupel gespeicherten Werte zu speichern und entweder vollständig oder bei dest nur Register mit ihnen, wenn möglich) zu halten:
using std::tie;
using std::make_tuple;
tie(ones, twos) = make_tuple((ones^n)^~twos, (ones & n) | (twos & ~n));
Beachten sie, dass die Typen von der rechten Seite Paar/Tupel haben die Zielwerte auf der linken Seite passen als Umwandlung hier nicht implizit ist.Wenn Probleme auftreten, führen Sie eine static_cast
auf der rechten Seite, std::make_tuple
die expliziten Typen erzählen oder einfach nur den Konstruktor für std::tuple
erfordert explizite Typen verwenden, wie:
using std::tie;
using std::tuple;
tie(ones, twos) = tuple<int,int>((ones^n)^~twos, (ones & n) | (twos & ~n));
'std :: swap (x, y);' ist der bevorzugte Weg. – chris
@chris: Eigentlich ist 'std :: swap (x, y)' der falsche *** Weg. – Mehrdad
@Mehrdad, guter Punkt. Mein Schwerpunkt lag auf der Verwendung der vorgefertigten Version, anstatt immer versuchen zu inline, aber das ist ein gültiges Problem. – chris