Speicherloch std::chrono::utc_clock?!
-
@wob sagte in Speicherloch std::chrono::utc_clock?!:
@It0101 sagte in Speicherloch std::chrono::utc_clock?!:
Warum verwendest du hier eine Referenz auf den temporären Wert, den now() zurück gibt?
Torsten verwendet eine konstante Referenz auf einen temporären Wert.
Die Lebensdauer des Rückgabewertes beschränkt sich auf das Statement selber, d.h. deine Referenz ist dann direkt ungültig.
Konstante Referenzen verlängern die Lebenszeit.
Siehe https://en.cppreference.com/w/cpp/language/lifetimeThe lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11), see reference initialization for details.
Okay, das war mir nicht bekannt. Ich halte das für falsch, weil es zu schlampigem Umgang mit Referenzen verführt, aber wenn der Standard es zulässt, dann ist das hinzunehmen
Zumal es in dem Fall unnötig ist.
-
@It0101
Ist aber ein ganz brauchbares Feature, finde ich, weil man sowas Problemlos machen kann:Something getSomething(); void consumeSomething(const Something& something) //somewhere in the code consumeSomething(getSomething())
Und der Standard garantiert das der Return Wert von
getSomething()
so lange lebt, wie es im Scope vonconsumeSomething
ist.
-
@hustbaer sagte in Speicherloch std::chrono::utc_clock?!:
signed main()
Dir ist klar dass das kein Mensch so schreibt, oder?
Aber OK, darum geht's ja nicht.
VLD scheine wohl keine konfigurierbare "ignore" Liste zu haben. Doof. Was du aber machen kannst, ist bei Programmstart explizit
std::chrono::utc_clock::now
aufzurufen, eingeklammert inVLDDisable()
undVLDEnable()
.Hm... das wäre durchaus eine Option. Schön ist es dennoch nicht
Vielleicht finde ich noch einen anderen Workaround, danke für eure Hilfe.
VG Torsten
-
@Schlangenmensch sagte in Speicherloch std::chrono::utc_clock?!:
@It0101
Ist aber ein ganz brauchbares Feature, finde ich, weil man sowas Problemlos machen kann:Something getSomething(); void consumeSomething(const Something& something) //somewhere in the code consumeSomething(getSomething())
Und der Standard garantiert das der Return Wert von
getSomething()
so lange lebt, wie es im Scope vonconsumeSomething
ist.Du hast recht. Dann habe ich das garantiert insgeheim auch schon verwendet.
-
@Schlangenmensch sagte in Speicherloch std::chrono::utc_clock?!:
@It0101
Ist aber ein ganz brauchbares Feature, finde ich, weil man sowas Problemlos machen kann:Something getSomething(); void consumeSomething(const Something& something) //somewhere in the code consumeSomething(getSomething())
Und der Standard garantiert das der Return Wert von
getSomething()
so lange lebt, wie es im Scope vonconsumeSomething
ist.Die Garantie hast du in deinem Beispiel sowieso, das hat mit der "life extension" durch Binden einer const-ref mMn. nixe zu tun. Temporäre Objekte werden vereinfacht gesagt erst mit dem nächsten
;
zerstört. Beispiel:#include <vector> #include <iostream> struct TempThing { ~TempThing() { std::cout << "~TempThing: " << s << std::endl; } std::string* getStringPtr() { return &s; } std::string s; }; void setString(std::string* s, char const* cstr) { *s = cstr; } std::string makeString() { return "Look ma, no const-ref!"; } TempThing makeTempThing() { return {}; } int main() { setString( makeTempThing() /* <- 1 */ .getStringPtr(), makeString() /* <- 2 */ .c_str()); }
Ausgabe:
~TempThing: Look ma, no const-ref!
Keine einzige cons-ref weit und breit (*), aber trotzdem garantiert dass hier alles wie gewünscht funktioniert. Die Returnwerte von (1) und (2) werden garantiert erst zerstört nachdem
setString
zurückgekehrt ist.
-
@hustbaer Aber:
void useTempThing(TempThing& thing) { std::cout << thing.s << std::endl; } int main() { useTempThing(makeTempThing()); }
kompiliert nicht da man keine non const ref an einen L-Value binden kann, während
void useTempThing(const TempThing& thing) { std::cout << thing.s << std::endl; } int main() { useTempThing(makeTempThing()); }
funktioniert.
Ich hatte irgendwie im Kopf, dass das mit der "life extension" zusammen hängt.Was aber machen kann, ist über einen const ref parameter einen const ref member setzen. Das wäre vielleicht als Beispiel schöner gewesen. Damit wird dann die Life Time über das nächste Semikolon hinaus verlängert(siehe Erklärung von @hustbaer unter dem Post)
-
@Schlangenmensch sagte in Speicherloch std::chrono::utc_clock?!:
Was aber machen kann, ist über einen const ref parameter einen const ref member setzen. Das wäre vielleicht als Beispiel schöner gewesen. Damit wird dann die Life Time über das nächste Semikolon hinaus verlängert
Nein, das geht nicht. Weil die Referenz um deren Lebenszeit es geht in dem Fall der Parameter ist, nicht irgendwas was man eventuell danach dann wiederrum mit dem Parameter initisalisiert.
Und auch das Temporary direkt an die Membervariable dranzuknoten geht nicht:
#include <vector> #include <iostream> struct TempThing { ~TempThing() { std::cout << "~TempThing" << std::endl; } }; TempThing makeTempThing() { return {}; } struct Foo { TempThing const& cr = makeTempThing(); ~Foo() { std::cout << "~Foo" << std::endl; } }; int main() { Foo f; std::cout << "Checkpoint" << std::endl; }
GCC (akzeptiert den Code):
~TempThing Checkpoint ~Foo
Clang (Compilerfehler):
<source>:14:8: error: reference member 'cr' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object struct Foo { ^~~ <source>:22:9: note: in implicit default constructor for 'Foo' first required here Foo f; ^ <source>:15:22: note: initializing field 'cr' with default member initializer TempThing const& cr = makeTempThing(); ^ ~~~~~~~~~~~~~~~
-
@hustbaer MSVC akzeptiert das auch... ^^ aber ich gebe mich geschlagen
-
@Schlangenmensch
Ich will nur zur Sicherheit nachfragen: dir ist aufgefallen dass GCC, obwohl er den Code akzeptiert, hier eben genau nicht wie u.U. vom Programmierer gewünscht die Lebenszeit des temporären Objekts verlängert. Korrekt? Also trotz dem dass GCC den Code akzeptiert, kann man damit nix sinnvoll anfangen. MSVC hab ich nicht probiert, ich traue mich aber einen beliebigen 5-stelligen Betrag in EUR wetten dass auch MSVC hier die Lebenszeit nicht verlängert.
-
@hustbaer
Ausgabe MSVC:Checkpoint ~Foo
Was ich allerdings analaog dazu gemacht habe:
#include <iostream> struct A { A(std::string c): content(c) {} std::string content; }; A getA() { return A("temp"); } struct B { void print() const { std::cout << a.content << std::endl; } const A& a = getA(); }; int main() { B b; b.print(); }
und das gibt mitr MSVC (16 aka 2019),
temp
aus. GCC und Clang kann ich gerade nur online testen, GCC scheint zu kompilieren, gibt aber nichts aus und Clang akzeptiert das nicht.
-
@Schlangenmensch
OK, hab's mir angesehen. MSVC generiert hier total kaputten Code. Er scheint hier einen statischen Speicherbereich für das "temporäre" Objekt zu verwenden an dasB::a
gebunden wird. Dort wird dann jedes mal wenn man einB
Objekt erzeugt, einA
Objekt hineinkonstruiert. Ohne das alteA
Objekt vorher zu zerstören:#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> struct A { explicit A(std::string const& s) { strcpy(content, s.c_str()); } ~A() { std::cout << "~A: " << content << std::endl; } char content[100]; }; A getA() { static int count = 0; count++; return A("temp" + std::to_string(count)); } struct B { void print() const { std::cout << a.content << std::endl; } const A& a = getA(); }; int main() { B b1; B b2; B b3; b1.print(); b2.print(); b3.print(); std::cout << (&b1.a) << std::endl; std::cout << (&b2.a) << std::endl; std::cout << (&b3.a) << std::endl; }
Output:
temp3 temp3 temp3 00007FF789984450 00007FF789984450 00007FF789984450
Völlig unbrauchbar.
Die einzig vernünftige "Implementierung" hat hier Clang.