Fehler in folly/ScopeGuard.h?
-
Da: https://github.com/facebook/folly/blob/master/folly/ScopeGuard.h
Wenn man schreibt (wie im Kommentar beschrieben)
ScopeGuard guard = makeGuard([&] { friends_.pop_back(); });ist das dann nicht UB wenn der Destruktor aufgerufen wird?
Meine Interpretation:
ScopeGuardist nur ein Typedef aufScopeGuardImplBase&&undmakeGuardgibt eine davon abgeleitete Klasse zurück. Der Destruktor ist nicht virtuell.
-
Ich muss nochmal drueber schauen ...
-
schkopa schrieb:
Meine Interpretation:
ScopeGuardist nur ein Typedef aufScopeGuardImplBase&&undmakeGuardgibt eine davon abgeleitete Klasse zurück. Der Destruktor ist nicht virtuell.Muss er auch nicht sein. Das Objekt wird ja nicht über die Referenz gelöscht. Es lebt im automatischen Speicher und verschwindet von ganz allein.
-
Ja die Frage ist, wie sich das Objekt an die rvalue reference binden laesst und warum es bis zum Ende des Scopes lebt.
-
knivil schrieb:
Ja die Frage ist, wie sich das Objekt an die rvalue reference binden laesst und warum es bis zum Ende des Scopes lebt.
Kann sehr gut sein, dass ich da die entsprechenden Regeln nicht kenne. Kannst du ein Stichwort nennen, krümelkacker?
-
C++11, 12.2 p 4-5
There are two contexts in which temporaries are destroyed at a different point than the end of the full expression. The first [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:- A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.
- A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
- The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
- A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the full-expression containing the new-initializer. [...] [ Note: This may introduce a dangling reference, and implementations are encouraged to issue a warning in such a case. —end note ]
Siehe auch Herb Sutter's Artikel:
A Candidate for the Most Important ConstIn C++11 gilt das eben auch für Rvalue-Referenzen. Das wird sogar in der Definition des for-range Loops ausgenutzt:
auto&& __range = range_expression;
-
Nicht schlecht Herr specht. Bin von ScopeGuard begeistert. Schoen wie RAII, Vererbung und Rvalue-Referenzen (neben Templates und std::function) hier zusammenarbeitet. Dabei ist Vererbung genutzt worden, um sowas wie type erasure zu realisieren.
Mir war schon klar, dass T& nicht an temporaere Objekte gebunden werden kann. Auf der anderen Seite wird ja immer gesagt, dass T&& auch erstmal normale Referenzen sind, hier aber ein deutlicher Unterschied besteht.
-
Also Referenzen und R-Value-Referenzen entscheinden sich in dem Punkt
The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
voneinander?
-
"temporaer" war wahrscheinlich der falsche Begriff. Auch kann ich mit dem zitierten Fetzen wenig anfangen, da der Bezug fehlt.
-
Decimad schrieb:
Also Referenzen und R-Value-Referenzen entscheinden sich in dem Punkt
The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
voneinander?
Nein, wieso?
struct foo{}; const foo& f() { return foo(); } foo&& g() { return foo(); } f(); // Referenz ist ungültig g(); // Referenz ist ungültig
-
Oder anders:
struct foo {}; foo make_foo() { return foo(); } foo& a = make_foo(); foo&& b = make_foo(); // a ungültige referenz, b gültige referenz?Ich komme hier gerade etwas ins Straucheln, weil ich halt mit dem genauen Wortlaut der Regeln mangels Vokabulardefinitionen nicht so viel anfangen kann.
Mich wundert halt, warum der Code von ScopeGuardImpl eine R-Value-Referenz als Lebensverländerer benutzt (benutzen muss?), statt einer normalen Referenz.Edit:
Hier stand Grütze. ("foo c = make_foo()"). Referenz wird ja genommen, damit man kein Type-Slicing betreibt.
-
Alternativ gingen ja auch auto oder auto&&.
Eine Lvalue-Referenz auf Nicht-Const wird nicht funktionieren, weil man solche Referenzen nicht so initialisiert bekommt, dass sie auf ein temporäres Objekt verweisen.
Eine Lvalue-Referenz auf Const ist da nicht angebracht, weil man das ScopeGuard-Objekt noch verändern können will (siehe dismiss-Funktion).
Das einzig Sinnige hier ist wirklich eine Rvalue-Referenz oder eben auto.
Decimad schrieb:
struct foo {}; foo make_foo() { return foo(); } foo& a = make_foo(); foo&& b = make_foo(); // a ungültige referenz, b gültige referenz?Bei a würde noch ein const fehlen, da du sonst die Lvalue-Referenz nicht initialisiert bekommst.
struct foo {}; foo make_foo(int v) { return foo(); } int main() { foo const& a = make_foo(); foo && b = make_foo(); return 0; }Hier bleiben die Referenzen a und b beide gültig, weil die Lebenszeit der temporären foo-Objekte verlängert wird.
-
krümelkacker schrieb:
[...]
Eine Lvalue-Referenz auf Nicht-Const wird nicht funktionieren, weil man solche Referenzen nicht so initialisiert bekommt, dass sie auf ein temporäres Objekt verweisen.
[...]Das ist mir jetzt mal wieder unangenehm, die Regel hätte ich ja nun kennen müssen -.- Schaue ich mir heute Abend mal an, warum dem so sein muss, verschwendet keine Mühe auf mich

Danke für die Klarstellung.