Vermeiden kopieren von Objekten mit der "return" - Anweisung
Ich habe eine sehr grundlegende Frage in C++.
Wie zu vermeiden, kopieren Sie, wenn Sie ein Objekt zurückgeben ?
Hier ist ein Beispiel :
std::vector<unsigned int> test(const unsigned int n)
{
std::vector<unsigned int> x;
for (unsigned int i = 0; i < n; ++i) {
x.push_back(i);
}
return x;
}
Als ich verstehen, wie C++ funktioniert, wird diese Funktion erstellen Sie 2 Vektoren : die lokale (x), und die Kopie von x, die zurückgegeben werden. Gibt es eine Möglichkeit zu vermeiden, die kopieren ? (und ich will nicht zurück einen Zeiger auf ein Objekt, aber das Objekt selbst)
Was wäre die syntax der Funktion mit "move-Semantik" (das Stand in den Kommentaren)?
- move-Semantik: www2.research.att.com/~bs/C++0xFAQ.html#rval
- Es wird nicht unbedingt eine Kopie erstellen. NRVO oder verschieben symantics kann das verhindern.
- Sie können sich auf Ihr compiler für die Durchführung NRVO Magie oder ausdrücklich verwenden Sie move-Semantik.
- Die "Kopie von x, die zurückgegeben werden" können konstruiert werden durch den übergang von x, oder den Bau erstellte, um sich auf das gleiche Objekt wie x. Die Semantik der Sprache bereits vermeiden Sie Kopien.
- In Antwort auf Ihre Bearbeiten - Sie müssen nicht zum ändern der syntax überhaupt. Alles, was förderfähig ist für copy elision verwenden müssen, bewegen den Bau (wenn der Bau noch nicht erstellte insgesamt).
- NRVO oder nicht, was ist, wenn der return-Typ kann nicht kopiert werden (D. H. Sie verursacht einen compile-Fehler)?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dieses Programm nutzen können, named return value optimization (NRVO). Siehe hier: http://en.wikipedia.org/wiki/Copy_elision
In C++11 move-Konstruktoren und assignment, die sind auch Billig. Lesen Sie ein tutorial hier: http://thbecker.net/articles/rvalue_references/section_01.html
Scheint es einige Verwirrung darüber, wie die RVO (Return Value Optimization) funktioniert.
Einem einfachen Beispiel:
Und seine Ausgabe an ideone:
Überraschend ?
Tatsächlich, das ist der Effekt, der RVO: das Objekt zurückgegeben werden wurde direkt im Ort in die Anrufer.
Wie ?
Traditionell, der Anrufer (
main
hier) Reservierung von Speicherplatz auf dem stack für return-Wert: der Rückkehr slot; die aufgerufene (create
hier) übergeben wird (irgendwie) die Adresse des return-slot zu kopieren Sie den Rückgabewert an. Der angerufene dann reservieren Sie einen eigenen Bereich für die lokale variable, in dem es erstellt das Ergebnis, wie für jede andere lokale variable verwenden, und dann kopiert diese in die return-slot auf derreturn
- Anweisung.RVO wird ausgelöst, wenn der compiler leitet sich vom code, der die variable erstellt werden können, direkt in die Rückkehr slot mit gleicher Semantik (als-ob-Regel).
Beachten Sie, dass dies solche gemeinsame Optimierung, es ist ausdrücklich in der weißen Liste aufgeführt, die von der Standard-und der compiler nicht haben, um sorgen über mögliche Nebenwirkungen der copy (oder move) Konstruktor.
Wann ?
Der compiler wird höchstwahrscheinlich mit einfachen Regeln, wie:
Im letzteren Fall (4), die Optimierung nicht angewendet werden, wenn
A
zurückgegeben wird, weil der compiler nicht bauen kanna
in der return-slot, wie er es brauchen kann für etwas anderes (je nach der Boolesche Bedingungb
).Eine einfache Faustregel ist so, dass:
RVO angewandt werden sollte, wenn kein anderer Kandidat für die Rückkehr slot erklärt wurde, vor der
return
- Anweisung.a
hat keine Nebenwirkungen, und, dass, die Erklärung kann nach unten verschoben werden unter derif
.-O2
oder-O3
)? Wie bereits erwähnt, dies ist eine Optimierung, so dass im debug-builds (-O0
) ist es unwahrscheinlich erscheinen; auch, da alle Optimierungen, die der compiler in manchen Fällen NICHT anzuwenden, aus Gründen, die ziemlich schwierig sein kann, um die göttliche.g++ -std=c++11 filename.cpp
. Ich denke, es ist-O0 standardmäßig, richtig? Nach Ihrem Kommentar, ich habe versucht, mit -O2 & -O3, aber Sie sind noch Verschieden. In einem anderen Beitrag habe ich gelesen, dass wir angeben müssen, die Kopie-Konstruktor manuell, für diese zu arbeiten.Named Return Value Optimization wird der job für Sie tun, da der compiler versucht, um redundante Kopie-Konstruktor und Destruktor-Aufrufe, während es zu benutzen.
mit return-Wert-Optimierung:
(in Fall, dass Sie wollen, um zu versuchen, es selbst für ein tieferes Verständnis, Blick auf dieses Beispiel von mir)
oder noch besser, wie Matthieu M. darauf hingewiesen, wenn Sie anrufen
test
innerhalb der gleichen Zeile, woy
deklariert ist, können Sie auch vermeiden, den Bau von redundanten Objekt und redundante Zuordnung als gut (x
wird gebaut im Speicher, woy
gespeichert werden soll):überprüfen Sie seine Antwort für besseres Verständnis der situation (Sie werden auch feststellen, dass diese Art von Optimierung kann nicht immer angewendet werden).
ODER könnte man den code so ändern, übergeben Sie die Referenz von Vektor-zu Ihrer Funktion, das wäre semantisch korrekter, während die Vermeidung kopieren:
y
vor der Zuweisung zu. Ihre Abfolge der Ereignisse richtig ist in diesem Fall jedoch würde ich dir empfehlen einfach direkt initialisiereny
zu vermeiden, erstellen Sie zwei Objekte, wo eine genügen würde. Sorry für die Störung.Compiler oft optimieren können entfernt werden die extra-Kopie für Sie (dies ist bekannt als return-Wert-Optimierung). Sehen https://isocpp.org/wiki/faq/ctors#return-by-value-optimization
Referenzierung, die es funktionieren würde.
Der move-Konstruktor ist garantiert verwendet werden, wenn NRVO nicht passieren
Daher, wenn Sie ein Objekt zurückgeben, mit move-Konstruktor (wie
std::vector
) von Wert ist, ist es garantiert nicht zu tun, eine vollständige Vektor-Kopie, selbst wenn der compiler nicht die optionale NRVO-Optimierung.Diese wird wie erwähnt durch zwei Benutzer erscheinen einflussreich in der C++ - Spezifikation selbst:
Nicht zufrieden mit mein Appell an Berühmtheit?
OK. Kann ich nicht ganz verstehen, die C++ - standard, aber ich kann verstehen, dass die Beispiele, die es hat! 😉
Unter Angabe der C++17 n4659 standard-Entwurf 15.8.3 [Klasse.kopieren.elision] "Kopieren/verschieben elision"
Ich weiß nicht, wie das "benutzt werden könnte" Wortwahl, aber ich denke, die Absicht ist, bedeuten, dass, wenn entweder "3.1" oder "3.2" halten, dann die rvalue zurück passieren müssen.
Dies ist ziemlich klar, auf die Kommentare im code für mich.
Pass by reference +
std::vector.resize(0)
für mehrere AnrufeWenn Sie mehrere Aufrufe
test
, ich glaube, das wäre etwas effizienter als es spart ein paarmalloc()
Anrufe + Umzug kopiert, wenn der Vektor verdoppelt in der Größe:gegeben, dass https://en.cppreference.com/w/cpp/container/vector/resize sagt:
und ich glaube nicht, dass Compiler sind in der Lage zur Optimierung des return-by-value-version um zu verhindern, dass die extra mallocs.
Auf der anderen Seite:
so, es ist ein trade-off.
Zunächst, Sie könnte erklären Sie Ihre Rückkehr Typ std::vector & in diesem Fall wird eine Referenz zurückgegeben werden, statt einer Kopie.
Könnte man auch definieren einen Zeiger, bauen Sie den Zeiger innerhalb der Methode Körper und dann wieder, dass der Zeiger (oder eine Kopie, die Zeiger korrekt zu sein).
Endlich viele C++ - Compiler können tun, return-Wert-Optimierung (http://en.wikipedia.org/wiki/Return_value_optimization) Beseitigung der temporären Objekt in einigen Fällen.