R-Value-Referenz als Funktionsparameter



  • Hallo Forum,

    im folgenden Beispiel nimmt die Funktion einen Parameter vom Typ r-value-Referenz auf Foo (ob das hier sinnvoll ist, sei einmal dahingestellt).

    void DoSomethingWithFoo(Foo&& param)
    {
        Foo foo1(param); // ruft copy ctor
        Foo foo2(std::move(param)); // ruft move ctor
    }
    

    Mir ist nicht ganz klar, warum der Parameter innerhalb der Funktion nicht direkt als r-value-Referenz behandelt wird. Warum muss ich mittels std::move (noch einmal) explizit nach r-value-Referenz casten, damit anstatt des copy-Konstruktors der move-Konstruktor aufgerufen wird?
    Scott Meyers schreibt, dass der Parameter selbst immer ein l-value ist, selbst wenn sein Typ eine r-value-Referenz ist. Das finde ich ein wenig verwirrend. Kann hier jemand zur Erhellung beitragen?

    Vielen Dank vorab!



  • eine rvalue referenz hat keinen Namen, wird sie einer Variablen zugewiesen ist es streng genommen kein RValue mehr.

    die Funktion hat eigentlich wenig Sinn es sei den es geht um forwarding und dann sollte man Meyers lesen, was du ja machst.



  • Meyers verweist auch darauf, dass alles, wovon man sich die Adresse holen kann, noramlerweise ein l-value ist. Das ist mir auch plausibel.
    Trotzdem irritiert mich hier irgendwie, dass etwas, was eigentlich vor allem dazu gedacht ist, "gemoved" zu werden (nämlich der Parameter vom Typ Foo&&) nochmal explizit in eine r-value-Referenz gecastet werden muss.

    Aber ich glaube, es ist mir trotzdem ein wenig klarer geworden:
    Übergibt man den Parameter by value, wird eine möglicherweise unerwünste Kopie erzeugt.
    Übergibt man den Parameter by (non const) reference, kann ein move innerhalb der Funktion böse Folgen haben.
    Übergibt man den Parameter als r-value-Referenz, hat man eine Kombination aus beidem. Man spart sich die Kopie bei der Übergabe und kann gleichzeitig in der Funktion bedenkenlos move nutzen.

    (Das Beispiel war nur theoretischer Natur. Dass es wenig sinnvoll war, ist mir schon klar.)

    Danke jedenfalls für die Antwort!



  • Die erneute Verwendung von std::move mag zwar etwas störend erscheinen, ich finde sie aber durchaus sinnvoll. Angenommen man müsste es nicht schreiben und der Code sähe wie folgt aus:

    void DoSomethingWithFoo(Foo&& param)
    {
        func1(param);  // nimmt func1 eine r-value oder l-value referenz?
        func2(param);  // param noch gültig?
    }
    

    Ohne explizites std::move kann man sich bei jeder Verwendung der R-Value Referenz nicht sicher sein ob die Operation gerade den Inhalt "geklaut" hat oder nicht.



  • Das Argument zieht und macht auch noch einmal klar, dass es sich eben bei dem Parameter selbst um einen l-value handelt (er hat einen Namen, eine Adresse und kann mehrfach verwendet werden), auch wenn sein Typ eine r-value-Referenz ist.

    Vielen Dank für die Antwort!


Anmelden zum Antworten