Temporary Lifetime



  • Disclaimer: Mir ist vollkommen klar, dass dieser Beispiel-Code wieder einige dazu verführen wird, die Sinnhaftigkeit meines Anliegens infrage zu stellen. Da es sich mit Code jedoch viel leichter zeigen lässt, um was es eigentlich geht und ich hier nun kein wunderprächtiges Beispiel aus dem Hut zaubern möchte, bei dem sich die eigentliche Frage kaum noch erkennen lässt, hier mal ein absolutes künstliches minimal Beispiel.

    std::string s = std::move(std::string{});
    

    Man stelle sich obigen Code weitaus komplexer, gegebenenfalls etwas weiter verschachtelt vor. Die Frage ist nun eigentlich: ist das valide oder dangled das?
    Meiner Erklärung wäre folgende: Das durch std::string{} erzeugte Objekt ist ein temporary, welches entsprechend an den rvalue-ref Parameter von std::move gebunden wird (dementsprechend seine Lebenszeit verlängert). Diese rvalue-ref wird von std::move entsprechend wieder zu einer rvalue-ref gecastet und zurückgegeben (dementsprechend zweite Verlängerung der Lifetime?!), bis es schlussendlich an einen ctor Parameter von s gebunden wird (3. Verlängerung). Daraus würde für mich folgen, dass das valide ist und nicht dangled.

    Afaik verlängern const lvalue-refs und rvalue-refs die Lebenszeit von temporaries.

    Erweitern wir das Beispiel aber noch ein wenig.

    struct foo
    {
        const std::string& s;
    };
    
    
    int main()
    {
        foo f{ .s = std::move(std::string{}) };
    
        std::cout << f.s;
    }
    

    Wie sieht es nun aus? Wird die Lebenszeit vom std::string-temporary nun ein viertes mal verlängert?

    Ich fände es super, wenn mich hier jemand ein wenig erleuchten könnte. Gerne auch mit einem Verweis auf den konkreten Part im Standard.

    Gruß,
    Dominic


  • Mod

    Ja, das ist korrekt. Siehe http://eel.is/c++draft/class.temporary#6.9

    The exceptions to this lifetime rule are:

    • A temporary object bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.
    • A temporary object bound to a reference element of an aggregate of class type initialized from a parenthesized expression-list ([dcl.init]) persists until the completion of the full-expression containing the expression-list.

    D.h. egal wie verschachtelt die Aufrufe sind, die Temporary lebt bis das statement abgeschlossen wurde (an welchem Punkt sowieso bereits von ihr gemoved worden sein sollte).

    Wie sieht es nun aus? Wird die Lebenszeit vom std::string-temporary nun ein viertes mal verlängert?

    Ja. Edit: Nein. Die Temporary ist ja an den Parameter von move gebunden. Haettest Du das move weggelassen, wuerde es funktionieren.

    Eine nennenswerte Ausnahme dieser Regel ist im zweiten Stichpunkt der oben zitierten Liste, wenn die Initialisierung mit runden statt geschweiften Klammern formuliert wird.

    foo f(std::string{}); // Im naechsten Statement dangled die Memberreferenz
    

    Keine Ahnung was die Absicht hinter dieser Ausnahme gewesen ist, anscheinend wollte man die Semantik von existierenden Konstruktoraufrufen nicht aendern indem man die Lebenszeit der
    Temporary erweitert.. (siehe https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0960r2.html)



  • Ok, vielen Dank dafür.
    Wenn ich dich richtig verstehe, dann wäre der Member s tatsächlich so lange valide, wie f gültig ist, sofern man den CTor nicht mit runden Klammern benutzt? Das irritiert mich dann doch ein wenig.
    Stutzig macht mich auch noch dieses (etwas veraltete) Statement von Herb Sutter. Note this only applies to stack-based references. It doesn’t work for references that are members of objects.
    https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
    Wir befinden uns mit f klar auf dem Stack, allerdings ist s hierbei ja ein member of object(s); diesem Satz nach dürfte das (meiner Verständnis nach) nicht mehr valide sein. Aber wahrscheinlich hat sich da in den letzten Jahren auch noch das ein oder andere Detail geändert.


  • Mod

    @DNKpp sagte in Temporary Lifetime:

    Stutzig macht mich auch noch dieses (etwas veraltete) Statement von Herb Sutter. Note this only applies to stack-based references. It doesn’t work for references that are members of objects.
    https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
    Wir befinden uns mit f klar auf dem Stack, allerdings ist s hierbei ja ein member of object(s); diesem Satz nach dürfte das (meiner Verständnis nach) nicht mehr valide sein. Aber wahrscheinlich hat sich da in den letzten Jahren auch noch das ein oder andere Detail geändert.

    Edit:
    Ne, aggregate initialization gab es tatsaechlich schon immer, auch fuer Referenzen. Da hat sich Herb scheinbar einfach verplappert.

    @DNKpp Ich hab mich uebrigens auch verplappert. Der move Fall oben erweitert die Lebenszeit definitiv nicht. Siehe meine korrigierte Antwort.


Log in to reply