NRVO or not?



  • Ich frage mich, ob bei dem folgenden Beispiel NRVO eintritt oder nicht

    auto foo(){
      Widget w1,w1
    
      //... do stuff to the objects
    
      return std::make_pair(w1, w1);
    }
    
    

    Einerseits habe ich zwei lvalues statt einem, die ich zurückgebe, andererseits aber wird ja eigentlich nur ein par zurückgegeben.

    Für Hilfe bin ich dankbar



  • @sewing sagte in NRVO or not?:

    Und kann man einem Paar keinen Namen geben?

    auto foo(){
      std::pair<Widget, Widget> p;
    
      //... do stuff to the objects
    
      return p:
    }
    


  • natürlich, also wäre mein Beispiel sogar noch sicherer copy elision, da das objekt direkt in place konstruiert wird?

    Hatte mich nur gefragt, ob RVO hier eintritt, da als Bedingungen ja gelten muss

    1. in allen return statements wird nur die gleiche Instanz zurückgegeben
    2. der Rückgabewert initialisiert auf caller-side direkt eine Objekt.

    Das ist hier demnach gegeben, nehme ich dann an. Danke für die Hilfe



  • @sewing sagte in NRVO or not?:

    natürlich, also wäre mein Beispiel sogar noch sicherer copy elision, da das objekt direkt in place konstruiert wird?

    Ja, allerdings nur das pair. Die Widgets werden kopiert bzw. gemoved. Wenn du es machst wie @manni66 gezeigt hat würde gar nichts kopiert bzw. gemoved.



  • ich verstehe den Unterschied nicht ganz. Bei Mannis snippet muss ich doch die bieden member des pairs auch durch assinment erst noch befüllen und gebe dann nen lvalue zurück, während std::make_pair nen rvalue zurückgibt



  • @sewing sagte in NRVO or not?:

    member des pairs auch durch assinment erst noch befüllen

    Was auch immer du damit meinst, das

    auto foo(){
      std::pair<Widget, Widget> p;
      return p:
    }
    

    ist vollständig.



  • auto foo(){
      std::pair<Widget, Widget> p;
    
      p.first = Widget{...}
      ...
    
      return p:
    }
    

    sowas meine ich. Und dabei verstehe ich nicht ganz, wieso das effizienter sein soll, als meine make_pair variante



  • @sewing sagte in NRVO or not?:

    p.first = Widget{...}

    wozu?



  • @sewing sagte in NRVO or not?:

    auto foo(){
      std::pair<Widget, Widget> p;
    
      p.first = Widget{...}
      ...
    
      return p:
    }
    

    sowas meine ich.

    Wo wird denn da bitte was kopiert? Ich sehe da bloss ne Zuweisung. Und wieso du die überhaupt machst weiss ich auch nicht. Kann ich auch nicht, weil ich nichtmal weiss was Widget ist und wie es sich so verhält.

    Wieso nicht einfach

    auto foo() {
      std::pair<Widget, Widget> p{ { a, b, c }, { d, e, f } };
    
      p.first.x = 123;
      p.first.y = 456;
      p.first.z = 789;
      ...
    
      return p;
    }
    

    ?

    Und dabei verstehe ich nicht ganz, wieso das effizienter sein soll, als meine make_pair variante

    Du hast nicht nach Effizienz gefragt sondern nach (N)RVO.



  • ok vielleicht formulier ich es anders. Ich suche nach einer Möglichkeit möglichst effizient zwei lvalues, die innerhalb eines function scopes angelegt und modifiziert werden, mittels return-by-value zurückzugeben.

    Meine Idee war eben

    return std::make_pair/make_tuple
    

    mit return value deduction via auto und structured bindings auf der call side



  • Und @manni66 hat dir hier in der 1. Antwort dieses Threads gezeigt wie es geht. Was genau ist dir dabei nicht klar?

    Vielleicht dass ein pair<Widget, Widget> aus zwei Widget-Objekten besteht (und NICHT aus Zeigern/Referenzen auf Widget-Objekte o.ä.)?

    Falls du noch Fragen hast zeig bitte konkreten Code, und nicht was wo du alles interessante mit "..." ersetzt hast.



  • naja ich will das pair nur zum zurückgeben benutzen. Davor sollen zwei unabhängige Objekte in der Funktion manipuliert werden. Danach suchte ich nur nach einer Möglichkeit die beiden Instanzen gemeinsam zurückzugeben.

    Daher möchte ich das pair auch nicht am Anfang der funktion schon allokieren



  • Ich verstehe bloss nicht wieso du das willst.
    Wo ist das Problem wenn du

        std::pair<Widget, Widget> p;
        Widget& w1 = p.first;
        Widget& w2 = p.second;
        // ...
        return p;
    

    schreibst?

    Ansonsten wirst du wohl oder übel die Widgets kopieren müssen, weil du sie ohne zu kopieren halt nicht in das pair reinbekommst. Dass du "nach" dem return gleich wieder ein "structured binding" verwendest um die beiden Teile auseinanderzupflücken ändert nichts daran dass es zwischendurch ein pair ist und dieses pair (und seine Teile) initialisiert werden müssen.

    Wobei es mich etwas wundert dass du kopierbare Widgets hast. Üblicherweise sind Widgets nicht kopierbar.



  • @hustbaer sagte in NRVO or not?:

    Wobei es mich etwas wundert dass du kopierbare Widgets hast. Üblicherweise sind Widgets nicht kopierbar.

    Eben. Mir fällt da auf Anhieb auch kein Anwendungsfall ein. Normalerweise sollten die auch gar nicht kopierbar sein. Widgets sind einfach da und gehören einem Parent.

    Was ist denn hier in dem Fall der Anwendungsfall für das Kopieren von Widgets?



  • Wahrscheinlich meint @Sewing mit "Widget" nur "irgendeine Klasse". Herb Sutter verwendet auch immer "widget" als Namen, wenn er eine unspezifizierte Klasse meint.



  • @wob sagte in NRVO or not?:

    Wahrscheinlich meint @Sewing mit "Widget" nur "irgendeine Klasse". Herb Sutter verwendet auch immer "widget" als Namen, wenn er eine unspezifizierte Klasse meint.

    Nur weil Herb Sutter solchen Quark macht und Begriffe verwendet, die bei vielen Entwicklern Assoziationen hervorrufen, muss das ja nicht gut sein. 😉



  • Widget ist in der Tat nur ein random class type.

    Aber mal zurück zum thema, ich dachte std::make_pair hätte nen overload der forwarding references hat.

    Wieso wird dann beim Aufruf mit zwei lvalues kopiert?

    edit: Ok verstanden. in make_pair wird als factory function auch nur nen std::pair erzeugt und dazu die konstruktoren der Widget (entweder copy oder move) aufgerufen.

    Mein Denkfehler war, zu übersehen, dass

    std::pair<Widget,Widget> p;
    

    schon die beiden widgets default constructed. also das was Manni schon sagte.

    Sehe ich das richtig, dass std::make_pair also ne reine convenience function ist, um ne explizite Angabe der template Argumente zu umgehen?

    Wenn dem so ist, dannmüsste mit C++17 class template argument deduction und deduction guides std::make_pair ja obsolet sein oder?

    Ich finde einfach generell eine pair definition zu Anfang der funtion unschön.

    Aber der workaround

    auto foo(){
      Widget w1, w2;
      ...
      return std::pair{std::ref(w1),std::cref(w2)};
    }
    

    funktioniert ja auch ohne jegliches kopieren 🙂



  • @sewing sagte in NRVO or not?:

    funktioniert ja auch ohne jegliches kopieren

    Ja dann ...



  • @sewing sagte in NRVO or not?:

    auto foo(){
      Widget w1, w2;
      ...
      return std::pair{std::ref(w1),std::cref(w2)};
    }
    

    funktioniert ja auch ohne jegliches kopieren 🙂

    Überleg nochmal. Wie sieht es nun mit der Gültigkeit der Objekte aus?

    Was spricht denn hier gegen? Wenn du dir in der Funktion am Anfang nicht sicher bist, welche Objekte du zurück gibst, dann tut's doch auch ein move in das pair hinein, oder nicht?

    std::pair<Widget, Widget> foo()
    {
      Widget w1,w2, w3, w4;
    
      // wir wählen jetzt 2 davon aus
    
      return { std::move(w2), std::move(w3) };
    }
    


  • Klar, wenn ich Referenzen auf temporaries nehme, ist das schlecht, aber auf der Call Side wird ja mit nem structured binding by value gearbeitet.
    Daher müsste das ja funktionieren oder?


Log in to reply