Die Bewegungssemantik ist nicht unbedingt eine große Verbesserung, wenn Sie einen Wert zurückgeben - und wenn Sie einen shared_ptr
(oder einen ähnlichen) verwenden, pessimieren Sie wahrscheinlich vorzeitig. In Wirklichkeit machen fast alle einigermaßen modernen Compiler das, was als Return Value Optimization (RVO) und Named Return Value Optimization (NRVO) bezeichnet wird. Dies bedeutet , dass wenn Sie einen Wert sind Rückkehr, statt Kopieren tatsächlich den Wert überhauptSie übergeben einfach einen versteckten Zeiger / Verweis darauf, wo der Wert nach der Rückgabe zugewiesen wird, und die Funktion verwendet diesen, um den Wert dort zu erstellen, wo er enden wird. Der C ++ - Standard enthält spezielle Vorkehrungen, um dies zu ermöglichen. Selbst wenn (zum Beispiel) Ihr Kopierkonstruktor sichtbare Nebenwirkungen hat, ist es nicht erforderlich, den Kopierkonstruktor zu verwenden, um den Wert zurückzugeben. Beispielsweise:
#include <vector>
#include <numeric>
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <iterator>
class X {
std::vector<int> a;
public:
X() {
std::generate_n(std::back_inserter(a), 32767, ::rand);
}
X(X const &x) {
a = x.a;
std::cout << "Copy ctor invoked\n";
}
int sum() { return std::accumulate(a.begin(), a.end(), 0); }
};
X func() {
return X();
}
int main() {
X x = func();
std::cout << "sum = " << x.sum();
return 0;
};
Die Grundidee hier ist ziemlich einfach: Erstellen Sie eine Klasse mit genügend Inhalten, die Sie nach Möglichkeit lieber nicht kopieren möchten std::vector
möchten wir mit 32767 zufälligen Ints füllen). Wir haben eine explizite Kopie, die uns anzeigt, wann / ob sie kopiert wird. Wir haben auch etwas mehr Code, um etwas mit den Zufallswerten im Objekt zu tun, so dass das Optimierungsprogramm (zumindest leicht) nicht alles an der Klasse eliminiert, nur weil es nichts tut.
Wir haben dann Code, um eines dieser Objekte von einer Funktion zurückzugeben, und verwenden dann die Summierung, um sicherzustellen, dass das Objekt wirklich erstellt und nicht nur vollständig ignoriert wird. Wenn wir es zumindest mit den neuesten / modernsten Compilern ausführen, stellen wir fest, dass der von uns geschriebene Kopierkonstruktor überhaupt nicht ausgeführt wird - und ja, ich bin mir ziemlich sicher, dass sogar eine schnelle Kopie mit einemshared_ptr
noch langsamer ist als das Kopieren überhaupt.
Durch Umzug können Sie eine ganze Reihe von Dingen erledigen, die Sie (direkt) ohne sie nicht erledigen könnten. Betrachten Sie den Zusammenführungsteil einer externen Zusammenführungssorte - Sie haben beispielsweise 8 Dateien, die Sie zusammenführen möchten. Idealerweise möchten Sie alle 8 dieser Dateien in ein vector
- einfügen, aber da vector
(ab C ++ 03) Elemente ifstream
kopiert werden müssen und s nicht kopiert werden können, stecken Sie mit einigen unique_ptr
/ fest shared_ptr
. oder etwas in dieser Reihenfolge, um sie in einen Vektor setzen zu können. Beachten Sie, dass der Compiler das auch dann nicht weiß, wenn wir (zum Beispiel) ein reserve
Leerzeichen in setzen, vector
so dass wir sicher sind, dass unser ifstream
s nie wirklich kopiert wird, so dass der Code nicht kompiliert wird, obwohl wir wissen, dass der Copy-Konstruktor es niemals sein wird trotzdem benutzt.
Auch wenn es immer noch nicht kopiert werden, in C ++ 11 ein ifstream
kann bewegt werden. In diesem Fall wendet sich die wahrscheinlich nicht wird immer bewegt werden, aber die Tatsache , dass sie hält notwendig sein könnte , wenn der Compiler glücklich, so dass wir unsere setzen können ifstream
Objekte in einvector
direkt, ohne Smart - Pointer - Hacks.
Ein Vektor, der es tut expandiert, ist ein ziemlich gutes Beispiel für eine Zeit, in der Bewegungssemantik wirklich nützlich sein kann / ist. In diesem Fall hilft RVO / NRVO nicht, da es sich nicht um den Rückgabewert einer Funktion (oder etwas sehr Ähnliches) handelt. Wir haben einen Vektor, der einige Objekte enthält, und wir möchten diese Objekte in einen neuen, größeren Speicherbereich verschieben.
In C ++ 03 wurden dazu Kopien der Objekte im neuen Speicher erstellt und anschließend die alten Objekte im alten Speicher gelöscht. Es war jedoch Zeitverschwendung, all diese Kopien nur zum Wegwerfen der alten Kopien anzufertigen. In C ++ 11 können Sie erwarten, dass sie stattdessen verschoben werden. Auf diese Weise können wir im Wesentlichen eine flache Kopie anstelle einer (im Allgemeinen viel langsameren) tiefen Kopie erstellen. Mit anderen Worten, mit einer Zeichenfolge oder einem Vektor (für nur einige Beispiele) kopieren wir nur die Zeiger in die Objekte, anstatt Kopien aller Daten zu erstellen, auf die sich diese Zeiger beziehen.
shared_ptr
nur um schnelles Kopieren zu ermöglichen) und wenn die Verschiebungssemantik das Gleiche ohne Beeinträchtigung von Codierung, Semantik und Sauberkeit erreichen kann.