Pass by value vs. pass by rvalue-Referenz

Wann sollte ich erklären, meine Funktion als:

void foo(Widget w);

im Gegensatz zu

void foo(Widget&& w);?

Nehme an, dies ist der einzige überlastung (wie in, ich wählen Sie eine oder das andere, nicht beide und keine anderen überladungen). Keine Schablonen beteiligt. Davon ausgehen, dass die Funktion foo müssen Sie der Besitzer der Widget (z.B. const Widget& ist nicht Teil dieser Diskussion). Ich bin nicht daran interessiert, jede Antwort, die außerhalb des Geltungsbereichs dieser Umstände. Siehe Nachtrag am Ende des Beitrags für warum diese Einschränkungen sind Teil der Frage.

Dem primären Unterschied, dass meine Kollegen und ich mit oben kommen kann ist, dass die rvalue-Referenz parameter zwingt Sie explizit über Kopien. Der Aufrufer ist dafür verantwortlich, eine explizite kopieren und dann Sie es mit std::move wenn Sie möchten, eine Kopie. In der pass-by-value Fall, die Kosten für die Kopie ist versteckt:

    //If foo is a pass by value function, calling + making a copy:
    Widget x{};
    foo(x); //Implicit copy
    //Not shown: continues to use x locally

    //If foo is a pass by rvalue reference function, calling + making a copy:
    Widget x{};
    //foo(x); //This would be a compiler error
    auto copy = x; //Explicit copy
    foo(std::move(copy));
    //Not shown: continues to use x locally

Andere als Unterschied. Andere als Leute zu zwingen, explizit über das kopieren und verändern, wie viel syntaktischen Zucker Sie erhalten, wenn Sie die Funktion aufrufen, wie sonst sind diese unterschiedlich? Was sagen Sie, anders über die Schnittstelle? Sind Sie mehr oder weniger effizient ist als eine andere?

Andere Dinge, die meine Kollegen und ich haben schon gedacht:

  • Der rvalue-Referenz parameter bedeutet, dass Sie kann bewegen das argument, aber nicht vorschreiben. Es ist möglich, dass das argument Sie übergeben beim Aufruf site wird in seinen ursprünglichen Zustand danach. Es ist auch möglich, die Funktion würde Essen/ändern das argument, ohne jemals Berufung ein move-Konstruktor aber davon ausgehen, dass, weil es war eine rvalue-Referenz, der Anrufer aufgegeben Kontrolle. Durch Wert übergeben, wenn Sie einziehen, müssen Sie davon ausgehen, dass eine Bewegung passiert; es gibt keine andere Wahl.
  • Vorausgesetzt, es ist kein elisions, einen einzigen move-Konstruktor-Aufruf eliminiert mit pass durch rvalue.
  • Der compiler hat eine bessere Chance zu elide kopiert/verschoben durch Wert übergeben. Kann das jemand untermauern diese Behauptung? Am besten mit einem link zu gcc.godbolt.org zeigt optimierte generierten code aus gcc/clang anstatt eine Zeile in der standard. Mein Versuch dieses zu zeigen war wohl nicht in der Lage, erfolgreich zu isolieren, die das Verhalten: https://godbolt.org/g/4yomtt

Nachtrag: warum bin ich einschränken von diesem problem so sehr?

  • Keine überlastungen - falls es andere überlastungen, das würde übergehen in eine Diskussion über pass-by-value vs eine Reihe von überladungen, die const-Referenz-und rvalue-Referenz, an welcher Stelle der Satz von überlastungen ist offensichtlich effizienter und gewinnt. Dies ist sehr gut bekannt, und daher nicht interessant.
  • Keine Vorlagen - ich bin nicht daran interessiert, wie die forwarding-Referenzen ins Bild passt. Wenn Sie eine Weiterleitung Referenz, rufen Sie std::forward sowieso. Das Ziel mit einer Spedition Referenz ist, an die Dinge, wie Sie Sie empfangen hat. Kopien sind nicht relevant, weil Sie nur pass ein lvalue statt. Es ist gut bekannt, und nicht interessant.
  • foo müssen Sie der Besitzer des Widget (aka keine const Widget&) - Wir reden hier nicht von nur-lese-Funktionen. Wenn die Funktion " schreibgeschützt war oder nicht brauchen, um eigenen oder verlängern die Lebensdauer der Widget, dann ist die Antwort trivial wird const Widget&, die wiederum ist gut bekannt, und nicht interessant. Ich habe auch erfahren Sie, warum wir nicht darüber reden wollen überlastungen.
  • Warum nicht einfach std::move anstatt einen Vermittler kopieren?
  • Ich könnte das machen, wenn ich nicht beabsichtigen, verwenden Sie die variable nach. Der Punkt ist, dass, wenn ich brauche eine Kopie, es ist jetzt explicit. Das Beispiel geht von einer Kopie ist notwendig für einige Grund.
  • Es hängt davon ab, was foo ist dabei mit dem parameter. Es ist ungewöhnlich für einen nicht-member-Funktion wie dieses zu brauchen, um sich den Besitz des Parameters.
  • Wenn Ihre Antwort würde variieren je nachdem, wie es verwendet wird, ich bin gespannt, wie würde Sie trennen. Das sagte Eigentums erfordert nicht die Speicherung auf dem heap/seine Lebensdauer wesentlich anders, nur, dass es eine Kopie beteiligten (oder, dass die Notwendigkeit für eine Kopie wird entfernt/optimierte Weg über einen Umzug). Es könnte sein, dass du das speichern der Werte in ein struct für später, könnte es sein, dass es ein iterator, die Sie brauchen, um voranzukommen.
  • Diese beiden Schnittstellen sind nicht austauschbar, wie pass-by-value nimmt auch l-Werte. Also ich bin mir nicht sicher, ob diejenigen, die verglichen werden können, sinnvoll, ohne die Angabe von weiteren Einschränkungen für die Nutzung.
  • Ich verstehe immer noch nicht deine Argumentation, dass const Widget& überlastungen. Sie sagen, mit den überlastungen ist effizienter und gewinnt, aber Sie wollen nicht zu betrachten, es sowieso? Und Sie sagen, Sie können nicht, verwenden Sie eine const Widget& überlastung, da die Funktion erfordert das "Eigentum" noch in den code, den Sie zeigen, es nimmt einem den Besitz einer Kopie, die Sie tun können, mit einem const Widget& überlastung.
  • Diese Frage ist sehr breit. Es macht einen großen Unterschied, ob die Funktion das Objekt ändern oder nicht, wenn Sie beschränken Sie es so, dass die Funktion immer das Objekt, ändert es machen würde, für immer mehr on-topic Antworten.

InformationsquelleAutor Mark | 2016-06-21
Schreibe einen Kommentar