Angenommen, ich möchte eine Funktion implementieren, die ein Objekt verarbeiten und ein neues (möglicherweise geändertes) Objekt zurückgeben soll. Ich möchte das in C + 11 so effizient wie möglich machen. Die Umgebung ist wie folgt:Effiziente Verwendung der Bewegungssemantik zusammen mit (N) RVO
class Object {
/* Implementation of Object */
Object & makeChanges();
};
Die Alternativen, die mir in den Sinn kommen, sind:
// First alternative:
Object process1(Object arg) { return arg.makeChanges(); }
// Second alternative:
Object process2(Object const & arg) { return Object(arg).makeChanges(); }
Object process2(Object && arg) { return std::move(arg.makeChanges()); }
// Third alternative:
Object process3(Object const & arg) {
Object retObj = arg; retObj.makeChanges(); return retObj;
}
Object process3(Object && arg) { std::move(return arg.makeChanges()); }
Hinweis: Ich möchte eine Umwicklungsfunktion wie process()
verwenden, da sie eine andere Arbeit tun, und ich möchte so viel Code wie möglich wiederverwenden.
Updates:
verwendete ich die makeChanges()
mit der gegebenen Unterschrift, da die Objekte, mit denen ich zu tun Methoden liefert mit dieser Art von Signatur. Ich nehme an, dass sie das für die Methodenverkettung benutzt haben. Ich habe auch die beiden genannten Syntaxfehler behoben. Danke, dass du sie rausgebracht hast. Ich habe auch eine dritte Alternative hinzugefügt, und ich werde die folgende Frage ruhen lassen.
Versuchen Sie diese mit clang [d. Object obj2 = process(obj);
] ergibt folgendes:
Erste Option ruft zwei Aufrufe des Kopierkonstruktors auf; eine für die Weitergabe des Arguments und eine für die Rückkehr. Man könnte stattdessen return std::move(..)
sagen und einen Aufruf des Kopierkonstruktors und einen Aufruf des Move-Konstruktors haben. Ich verstehe, dass RVO einen dieser Aufrufe nicht loswerden kann, weil es sich um den Funktionsparameter handelt.
In der zweiten Option haben wir noch zwei Aufrufe an den Kopierkonstruktor. Hier machen wir einen expliziten Anruf und man wird während der Rückkehr gemacht. Ich hatte damit gerechnet, dass RVO eingreifen und das Letztere loswerden würde, da das Objekt, das wir zurückgeben, ein anderes Objekt als das Argument ist. Es ist jedoch nicht passiert.
In der dritten Option haben wir nur einen Aufruf an den Copy-Konstruktor und das ist der explizite. (N) RVO eliminiert den Aufruf des Kopierkonstruktors, den wir für die Rückgabe ausführen würden.
Meine Fragen sind:
- (beantwortet) Warum Kick RVO in der letzten Option und nicht die zweite?
- Gibt es einen besseren Weg, dies zu tun?
- Hätten wir in einer temporären, 2. und 3. Optionen übergeben würde einen Move-Konstruktor aufrufen, während Sie zurückkehren. Ist es möglich, das mit (N) RVO zu eliminieren?
Vielen Dank!
Warum würde 'makeChanges' ein' Objekt & 'zurückgeben? Es sollte entweder nichts zurückgeben und eine mutierende Funktion sein oder es sollte "const" sein und ein neues Objekt nach Wert zurückgeben. Derzeit, weil es _nicht_ 'const' ist, sind Ihre erste und zweite aufgelistete Optionen nicht einmal kompilierbar, weil Sie eine nicht-konstante Elementfunktion für ein konstantes Objekt aufrufen. Effektiv macht dies Ihre Frage ziemlich unsinnig ohne eine Begründung für die aktuelle Signatur "makeChanges". – ildjarn
@ildjarn: Danke für die Kommentare. Ich habe Änderungen vorgenommen und die Frage gestellt. Ich denke, mir ist immer noch nicht klar, wie/wann RVO ansetzt. Ich würde gerne deine Ideen und Vorschläge hören. – iheap