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 durchstd::string{}erzeugte Objekt ist ein temporary, welches entsprechend an denrvalue-refParameter vonstd::movegebunden wird (dementsprechend seine Lebenszeit verlängert). Dieservalue-refwird vonstd::moveentsprechend wieder zu einerrvalue-refgecastet 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-refsundrvalue-refsdie 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
-
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 vonmovegebunden. 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 MemberreferenzKeine 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 Memberstatsächlich so lange valide, wiefgü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 mitfklar auf dem Stack, allerdings istshierbei ja einmember 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.
-
@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
moveFall oben erweitert die Lebenszeit definitiv nicht. Siehe meine korrigierte Antwort.