Lambdas und erfassen von verweisen auf lokale Variablen : der Zugriff auf die nach dem Umfang
Leite ich meine lokale Variablen per Referenz zu zwei lambda. Ich nenne diese Lambda-Ausdrücke außerhalb der Funktion Bereich. Ist dies undefined
?
std::pair<std::function<int()>, std::function<int()>> addSome() {
int a = 0, b = 0;
return std::make_pair([&a,&b] {
++a; ++b;
return a+b;
}, [&a, &b] {
return a;
});
}
int main() {
auto f = addSome();
std::cout << f.first() << " " << f.second();
return 0;
}
Wenn es nicht ist, jedoch änderungen in einem lambda-Ausdruck nicht in den anderen reflektiert lambda.
Bin ich Missverständnis pass-by-reference im Rahmen der lambdas ?
Schreibe ich die Variablen und es scheint zu funktionieren gut mit keine runtime-Fehler mit Ausgabe
2 0
. Wenn es funktioniert, dann würde ich erwarten, dass die Ausgabe 2 1
.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ja, führt dies zu undefiniertem Verhalten. Die lambdas werden Referenz-stack-allokierten Objekte, die gegangen sind, aus der Umfang. (Technisch, wie ich es verstehe, ist das Verhalten definiert, bis die lambdas Zugriff
a
- und/oderb
. Wenn Sie nie aufrufen der zurückgegebene Lambda-Ausdrücke, dann ist es nicht UB.)Dies ist undefiniert das Verhalten der gleichen Weise, dass es ist Undefiniertes Verhalten für die Rückgabe einer Referenz auf ein stack-allokierten lokalen und verwenden Sie dann die Referenz nach den örtlichen Bereich verlässt, außer dass in diesem Fall es wird verschleiert ein wenig durch die lambda.
Weiter, beachten Sie, dass die Reihenfolge, in der der Lambda-Ausdrücke aufgerufen werden, ist nicht angegeben -- der compiler ist kostenlos aufrufen
f.second()
vorf.first()
denn beide sind Teil der gleichen vollen Ausdruck. Deshalb, auch wenn wir fix den undefinierten Verhalten durch die Verwendung von Referenzen auf Objekte zerstört, beide2 0
und2 1
sind noch gültig Ausgänge von diesem Programm, und was Sie bekommen, hängt davon ab, in welcher Reihenfolge der compiler entscheidet zur Ausführung der Lambda-Ausdrücke. Beachten Sie, dass dies nicht zu undefiniertem Verhalten, denn der compiler kann nicht überhaupt nichts, sondern es hat einfach eine gewisse Freiheit in der Entscheidung, die Reihenfolge, in der gewisse Dinge zu tun.(Beachten Sie, dass
<<
in Ihremmain()
Funktion ist das aufrufen eines benutzerdefiniertenoperator<<
Funktion, und die Reihenfolge, in der Funktionsargumente ausgewertet werden, ist nicht spezifiziert. Compiler sind frei zu emittieren code, der wertet alle Funktionsargumente in der gleichen full-expression in beliebiger Reihenfolge, mit der Einschränkung, dass alle Argumente einer Funktion ausgewertet werden muss, bevor die Funktion aufgerufen wird.)Zu lösen das erste problem, verwenden Sie
std::shared_ptr
zum erstellen einer Referenz-gezählt Objekt. Capture mit dieser shared-pointer von Wert, und die lambdas halten Sie das Spitze-zu-Objekt erhalten, solange Sie (und alle Kopien) existieren. Dieser heap-Objekt zugeordnet ist, wo wir speichern den freigegebenen Zustand dera
undb
.Fix das zweite problem, bewerten jede lambda-Ausdruck in eine separate Anweisung.
Hier ist dein code umgeschrieben, mit dem undefinierten Verhalten fest, und mit
f.first()
garantiert aufgerufen werden, bevorf.second()
:(Sehen Sie es ausführen.)
Leider C++ Lambda-Ausdrücke erfassen kann, die durch Verweis, aber Sie lösen nicht die "nach oben funarg problem".
Dadurch würde verlangen der Zuweisung erfasst einheimischen in "Zellen" und die garbage-collection oder "reference counting" für die Freigabe. C++ ist, es nicht zu tun und leider ist dieses make C++ lambdas viel weniger nützlich und gefährlicher als in anderen Sprachen wie Lisp, Python oder Javascript.
Genauer gesagt in meiner Erfahrung, die Sie auf jeden Fall vermeiden sollten implizite erfassen, durch einen Verweis (D. H. mit der
[&](…){…}
form) für lambda-Objekte, die das überleben der lokalen Bereich, weil das ist ein Rezept für random segfaults später während der Wartung.Immer planen Sie sorgfältig über das, was zu erfassen ist und wie und über die Lebensdauer der erfassten Referenzen.
Natürlich ist es sicher, um alles zu erfassen, die durch Verweis mit
[&]
wenn alles, was Sie tun, ist einfach die lambda im gleichen Umfang zu pass-code-Beispiel für algorithmen wiestd::sort
die zimmerreserviereung, ohne das definieren von benannten Komparator-Funktion außerhalb der Funktion.Einen Ansatz, der arbeiten kann, manchmal ist die Erfassung von Wert eine
shared_ptr
zu einem heap-zugewiesenen Zustand. Dies ist im Grunde die Umsetzung von hand, was Python nicht automatisch (aber achten Sie auf Referenz Zyklen um Speicherverluste zu vermeiden: Python einen garbage collector, C++ nicht).[&]
ist in Ordnung, wenn die Lebensdauer der Schließung und deren Kopien ist beschränkt auf den umschließenden{}
, sonst ist es unsicher oder gefährlich. Als solche eingeschränkte lebenslange Sperrungen sind Häufig, vermeiden Sie es als eine Allgemeine Regel scheint übertrieben?[&]
ist vollkommen in Ordnung nur für Lambda-Ausdrücke, die verwendet werden, sofort.Wenn Sie gehen aus dem Rahmen. Dadurch wird eine Kopie Ihrer einheimischen.
Sind, wenn Sie in den Rahmen , besser zu nutzen, durch Verweise
&
ist sicher, während in der zweiten es sollte=
.