RValue reference - wann nötig?



  • hustbaer schrieb:

    "richtiger" ist wohl Ansichtssache.
    Im Fall von std::vector wird wohl beides zum gewünschten Ergebnis führen.

    Ich würde wohl die vector&& Variante vorziehen, da diese auch mit Typen funktioniert die keinen "move-ctor" haben.

    In wieweit würde das denn besser funktionieren? Wenn der Typ des übergebenden Objektes keinen move-ctor hat, dann bleibt dem Compiler ja nicht anderes übrig als zu kopieren?!
    Falls der Typ doch einen move-ctor hat, dann müsste ich ja denoch, solange das übergebene Objekt auf Aufruferseite benannt ist, ein explizites move nutzen. Falls das Objekt keinen Namen hat, dann wird implizit gemovt, egal, ob nun der Argumentstyp bar oder bar&& ist. Ich sehe also keinen Unterschied zwischen Argumentstyp bar oder bar&&.



  • RudolfRenntier schrieb:

    hustbaer schrieb:

    "richtiger" ist wohl Ansichtssache.
    Im Fall von std::vector wird wohl beides zum gewünschten Ergebnis führen.

    Ich würde wohl die vector&& Variante vorziehen, da diese auch mit Typen funktioniert die keinen "move-ctor" haben.

    Wenn der Typ des übergebenden Objektes keinen move-ctor hat, dann bleibt dem Compiler ja nicht anderes übrig als zu kopieren?!

    So wie ich rvalue-references verstehe wird hier garnix kopiert, sondern einfach die rvalue-reference an die rvalue gebunden, ganz ohne dass irgendein copy-ctor oder move-ctor aufgerufen wird. Genauso wie es auch passieren würde wenn man eine lvalue an eine lvalue-reference bindet, da wird ja auch nix kopiert oder gemoved.

    Was ich allerdings vergessen hatte: rvalue-references dürfen auch an lvalues gebunden werden (!), was die Verwendung von rvalue-references etwas "gefährlich" macht (wenn man nicht zusätzlich einen lvalue-reference overload definiert, denn lvalues bevorzugen natürlich den lvalue-overload).

    Wenn es sich um std::vector handelt, und man den Inhalt in der Funktion ändern möchte, ohne dass der Aufrufer irgendwas davon merkt, dann ist es vermutlich einfacher und vernünftiger sich auf die in std::vector "eingebaute" move-semantics zu verlassen, und einfach "void foo(vector<int> v)" zu schreiben.

    EDIT: OK, ich lese gerade, dass noch darüber diskutiert wird, ob man es erlauben soll, rvalue-references an lvalues zu binden. Im derzeitigen Draft ist es so drinnen (erlaubt), und bestehende Implementierungen handhaben das auch so, ist aber noch nicht ausgeredet. IMO wäre ja die bessere Variante es nicht zu erlauben, weil es viel "intuitiver" für mich wäre. Wenn es erlaubt ist, und man vergisst zu jeder Funktion die eine rvalue-reference akzeptiert zusätzlich einen lvalue-reference overload zu machen, dann kann das schnell dazu führen dass man Objekte verändert die man eigentlich garnicht verändern wollte.



  • Was zur Hölle soll vector&& sein???



  • wtf0r schrieb:

    Was zur Hölle soll vector&& sein???

    das wird die hier keiner verraten, weil jeder erwartet, daß du zuerst mal schnell "RValue reference" in google eingibst.



  • Kommt das im neuen Standard oder geht das bereits jetzt?



  • wtf0r schrieb:

    Kommt das im neuen Standard oder geht das bereits jetzt?

    beides, wenn du einen netten compiler hast.



  • Hi,

    sorry Jungs, Ihr habt mich abgehängt. Könnt Ihr mir mal ein kleines Beispiel geben für:

    RudolfRenntier schrieb:

    ..
    ich habe eine Funktion, die einen großen Container, z.B. einen std::vector übergeben bekommt.
    Intern geht die Funktion alle Elemente des Containers durch und ändert diese.
    Diese Änderungen sollen dem Aufrufer nicht zugänglich sein.

    Wird also der Funktion ein lvalue Container übergeben, muss der Container kopiert werden.
    Falls der Funktion ein rvalue Container übergeben wird, soll der Container implizit gemovt werden....

    Das verstehe ich nicht - zumal ich nicht weiß, wann hier "Rudolfs Anforderung", "Standardgemäßes Vorgehen", "logische Folge", ... gemeint ist, wenn ein Ablauf beschrieben wird.

    Gruß,

    Simon2.

    P.S.: Was ist nun mit camper?



  • Simon2 schrieb:

    P.S.: Was ist nun mit camper?

    der wird halt sein studium beendet haben und hat jetzt keine lust mehr



  • RudolfRenntier schrieb:

    Wird also der Funktion ein lvalue Container übergeben, muss der Container kopiert werden.
    Falls der Funktion ein rvalue Container übergeben wird, soll der Container implizit gemovt werden.
    Was ist nun richtig(er)?
    void foo(vector<bar>);
    oder
    void foo(vector<bar>&&);
    In meinen Augen sollte beides gehen.
    Danke :xmas2:

    in meinen augen mußt du sogar beides nehmen und durch die überladung feststellen, ob du kopieren mußt.

    void fooAndDestroy(vector<bar>& v)
    {//Sachen auf v machen und dabei v zerstören
    }
    void foo(vector<bar>&& v)
    {
     fooAndDestroy(v);
    }
    void foo(vector<bar>& v)
    {
     vector<bar> v2=v;
     fooAndDestroy(v2);
    }
    

    oder ums ein bißchen häßlicher zu gestalten, dazu ist c++ ja da:

    void foo(vector<bar>&& v)
    {//Sachen auf v machen und dabei v zerstören
    }
    void foo(vector<bar>& v)
    {
     foo(vector<bar>(v));
    }
    


  • hustbaer schrieb:

    RudolfRenntier schrieb:

    hustbaer schrieb:

    "richtiger" ist wohl Ansichtssache.
    Im Fall von std::vector wird wohl beides zum gewünschten Ergebnis führen.

    Ich würde wohl die vector&& Variante vorziehen, da diese auch mit Typen funktioniert die keinen "move-ctor" haben.

    Wenn der Typ des übergebenden Objektes keinen move-ctor hat, dann bleibt dem Compiler ja nicht anderes übrig als zu kopieren?!

    So wie ich rvalue-references verstehe wird hier garnix kopiert, sondern einfach die rvalue-reference an die rvalue gebunden, ganz ohne dass irgendein copy-ctor oder move-ctor aufgerufen wird. Genauso wie es auch passieren würde wenn man eine lvalue an eine lvalue-reference bindet, da wird ja auch nix kopiert oder gemoved.

    Stimmt. Das war mir entfallen.

    hustbaer schrieb:

    EDIT: OK, ich lese gerade, dass noch darüber diskutiert wird, ob man es erlauben soll, rvalue-references an lvalues zu binden. Im derzeitigen Draft ist es so drinnen (erlaubt), und bestehende Implementierungen handhaben das auch so, ist aber noch nicht ausgeredet. IMO wäre ja die bessere Variante es nicht zu erlauben, weil es viel "intuitiver" für mich wäre. Wenn es erlaubt ist, und man vergisst zu jeder Funktion die eine rvalue-reference akzeptiert zusätzlich einen lvalue-reference overload zu machen, dann kann das schnell dazu führen dass man Objekte verändert die man eigentlich garnicht verändern wollte.

    Naja, IMO wär' es dann aber einfacher, jeder "großen" Klasse einen Move-Ctor hinzuzufügen (die relevanten Klasse sind ja in der Regel recht überschaubar), und dann eben einfach das Argument ganz ohne Referenz, weder l noch r, zu definieren.
    Würde nun letzendlich aufs gleiche hinauskommen, und man hat um einiges weniger Redunanz durch weniger Überladungen und der Quelltext bleibt lesbar?!

    Naja, was solls. Wenn der Standard eh' noch nicht in der Hinsicht fest ist, dann lass' ich erstmal die Finger von solchen Spezialfällen. Da warte ich lieber, bis ich die neue Auflage von 'The C++ Programming language' auf meinem Tisch habe.

    Simon2 schrieb:

    RudolfRenntier schrieb:

    ..
    ich habe eine Funktion, die einen großen Container, z.B. einen std::vector übergeben bekommt.
    Intern geht die Funktion alle Elemente des Containers durch und ändert diese.
    Diese Änderungen sollen dem Aufrufer nicht zugänglich sein.

    Wird also der Funktion ein lvalue Container übergeben, muss der Container kopiert werden.
    Falls der Funktion ein rvalue Container übergeben wird, soll der Container implizit gemovt werden....

    Das verstehe ich nicht - zumal ich nicht weiß, wann hier "Rudolfs Anforderung", "Standardgemäßes Vorgehen", "logische Folge", ... gemeint ist, wenn ein Ablauf beschrieben wird.

    Anforderung war, dass ein großes nicht weiter definiertes Objekt einer Funktion so performant wie möglich übergeben wird. Dabei soll die Funktion das Objekt verändern (e.g. nicht-const Funktionen aufrufen), ohne dass der Aufrufer diese Änderung bei "seinem" übergebenen Objekt mitbekommt.

    Meine Frage war das Standartgemäßes Vorgehen in meinem Fall. Dabei bezog ich mich einerseits auf den wirklichen C++ Standard (Was passiert in meinem Fall, wenn ich rvalue reference als Argumentstyp habe?) sowie aber auch, was in meinem Fall in Hinsicht auf den neuen Standard das Standardvorgehen ist (Die Problemstellung ist ja sicher schon häufig aufgetreten und es gibt ja sicher eine Patentregel).

    logische Folge? A priori kommst du IMO bei solchen Geschichten nicht weit. Siehe Hustbaers post und das lvalue in rvalue konvertierungs Problem. Das hier ist eher Konvention als Logik.

    weißkeiner schrieb:

    Simon2 schrieb:

    P.S.: Was ist nun mit camper?

    der wird halt sein studium beendet haben und hat jetzt keine lust mehr

    Was hat er eigentlich studiert? Philosophie?



  • hustbaer schrieb:

    RudolfRenntier
    [quote="hustbaer"]
    EDIT: OK, ich lese gerade, dass noch darüber diskutiert wird, ob man es erlauben soll, rvalue-references an lvalues zu binden. Im derzeitigen Draft ist es so drinnen (erlaubt), und bestehende Implementierungen handhaben das auch so, ist aber noch nicht ausgeredet. IMO wäre ja die bessere Variante es nicht zu erlauben, weil es viel "intuitiver" für mich wäre. Wenn es erlaubt ist, und man vergisst zu jeder Funktion die eine rvalue-reference akzeptiert zusätzlich einen lvalue-reference overload zu machen, dann kann das schnell dazu führen dass man Objekte verändert die man eigentlich garnicht verändern wollte.

    Naja, IMO wär' es dann aber einfacher, jeder "großen" Klasse einen Move-Ctor hinzuzufügen (die relevanten Klasse sind ja in der Regel recht überschaubar), und dann eben einfach das Argument ganz ohne Referenz, weder l noch r, zu definieren.
    Würde nun letzendlich aufs gleiche hinauskommen, und man hat um einiges weniger Redunanz durch weniger Überladungen und der Quelltext bleibt lesbar?!

    Naja. Ich könnte mir vorstellen, dass es vielleicht in manchen "low-level" Klassen Fälle geben könnte, wo man eine rvalue eines unbekannten Typs hat, die man einer Funktion übergeben möchte, so dass diese dann darin "rumfummeln" kann. Ich denke da an Klassen die man typischerweise in der C++ Standard Library oder in Boost oder ähnlichen Libraries finden wird. In diesen Fällen kann ich mir vorstellen dass es u.U. sinnvoll sein kann, direkt Funktionen zu verwenden die rvalue-references als Parameter nehmen. Dann müssten auch Klassen von etwas einfacher gestrickten Programmierern, die sich mit rvalue-referencen und move-semantics nicht auseinandersetzen wollen, nicht so oft kopiert werden. Konkretes Beispiel kann ich keins nennen -- denke nur falls es solche Fälle gibt würde es Sinn machen.

    Wie ich aber schon sagte: solange es erlaubt ist rvalue-references an lvalues zu binden, und der Normalfall eh der ist dass die Klasse entweder billig zu kopieren, oder billig zu "moven" ist, dann würde ich es eher vorziehen pass-by-value zu machen, da man so auf weniger achten muss.

    Ich finde auch die ganze Debatte um rvalue-references, dass sie "keiner" versteht, dass die Leute "Angst dafor haben" das in C++ Kursen unterrichten zu müssen etc. ziemlich doof: rvalue-references sind IMO etwas was es ermöglicht performantere Libraries zu schreiben. Ein typischer C++ Programmierer braucht IMO nichtmal zu wissen dass es sie gibt. Es reicht IMO dass man ihm sagt dass es ab jetzt nichtmehr "total böse" ist einen std::string oder std::vector als Returntyp zu verwenden, oder er ab jetzt einen std::fstream überhaupt erst als Returntyp verwenden *kann*. Oder einen std::scoped_ptr (oder heisst der im Standard unique_ptr?) in einen std::vector stecken, was ohne move-semantics ja nicht möglich wäre.


Anmelden zum Antworten