Lambdas und Erfassung durch Referenz lokale Variablen: Zugriff nach dem Bereich

8

Ich übergebe meine lokalen Variablen mit Bezug auf zwei Lambda. Ich rufe diese Lambdas außerhalb des Funktionsumfangs auf. Ist das undefined ?

%Vor%

Wenn es nicht ist, werden Änderungen in einem Lambda nicht in anderem Lambda widergespiegelt.

Missverständnis ich Pass-by-Reference im Zusammenhang mit Lambdas?

Ich schreibe in die Variablen und es scheint zu funktionieren ohne Laufzeitfehler bei der Ausgabe

2 0 . Wenn es funktioniert, würde ich Ausgabe 2 1 erwarten.

    
Ashish Negi 05.01.2015, 07:42
quelle

3 Antworten

18

Ja, dies führt zu undefiniertem Verhalten. Die Lambdas referenzieren stapelbelegte Objekte, die den Gültigkeitsbereich verlassen haben. (Technisch, wie ich es verstehe, ist das Verhalten definiert, bis der Lambdas Zugriff a und / oder b . Wenn Sie nie die zurückgegebenen Lambdas aufrufen, dann gibt es keine UB.)

Dies ist ein undefiniertes Verhalten auf die gleiche Weise wie ein undefiniertes Verhalten, um einen Verweis auf einen stack-allokierten Local zurückzugeben und dann diesen Verweis zu verwenden, nachdem der lokale außerhalb des Bereichs liegt, außer dass er in diesem Fall ein wenig vom Lambda verschleiert wird .

Beachten Sie außerdem, dass die Reihenfolge, in der die Lambdas aufgerufen werden, nicht spezifiziert ist - der Compiler kann f.second() vor f.first() aufrufen, weil beide Teil desselben Ausdrucks sind. Selbst wenn wir das undefinierte Verhalten beheben, das durch die Verwendung von Verweisen auf zerstörte Objekte verursacht wird, sind sowohl 2 0 als auch 2 1 noch gültige Ausgaben von diesem Programm, und die Sie erhalten, hängt von der Reihenfolge ab Ihr Compiler entscheidet, die Lambdas auszuführen. Beachten Sie, dass dies nicht undefiniertes Verhalten ist, weil der Compiler überhaupt nichts tun kann , sondern einfach eine gewisse Freiheit bei der Entscheidung für die Reihenfolge hat einige Dinge .

(Beachten Sie, dass << in Ihrer Funktion main() eine benutzerdefinierte operator<< -Funktion aufruft und die Reihenfolge, in der Funktionsargumente ausgewertet werden, nicht spezifiziert ist. Compiler können Code ausgeben, der die gesamte Funktion auswertet Argumente innerhalb desselben Ausdrucks in beliebiger Reihenfolge mit der Einschränkung, dass alle Argumente für eine Funktion ausgewertet werden müssen, bevor diese Funktion aufgerufen wird.)

Um das erste Problem zu beheben, verwenden Sie std::shared_ptr , um ein Referenzobjekt zu erstellen. Erfassen Sie diesen freigegebenen Zeiger nach Wert, und die Lambdas halten das Objekt, auf das verwiesen wird, am Leben, solange sie (und Kopien davon) existieren. In diesem Heap-allokierten Objekt speichern wir den gemeinsamen Status von a und b .

Um das zweite Problem zu beheben, werten Sie jedes Lambda in einer separaten Anweisung aus.

Hier wird Ihr Code mit dem undefinierten Verhalten neu geschrieben und mit f.first() wird garantiert vor f.second() :

aufgerufen %Vor%

( Lassen Sie es laufen .)

    
cdhowie 05.01.2015, 07:45
quelle
7

Leider können C ++ Lambdas per Referenz erfassen, lösen aber nicht das " Problem nach oben nach oben ".

Dies würde erfordern die Zuordnung gefangener Einheimischer in "Zellen" und Garbage Collection oder Referenzzählung für die Aufhebung der Zuordnung. C ++ macht das nicht und das macht C ++ lambdas viel weniger nützlich und gefährlicher als in anderen Sprachen wie Lisp, Python oder Javascript.

Nach meiner Erfahrung sollten Sie unbedingt eine implizite Erfassung durch Referenz (d. h. mit dem [&](…){…} -Formular) für Lambda-Objekte vermeiden, die den lokalen Gültigkeitsbereich überstehen, weil dies ein Rezept für zufällige Segfaults später während der Wartung ist.

Planen Sie immer sorgfältig darüber, was erfasst werden soll und wie und über die Lebensdauer erfasster Referenzen.

(Natürlich ist es sicher, alles durch Referenz zu erfassen, wenn Sie nur das Lambda im gleichen Bereich verwenden, um Code an Algorithmen wie std::sort zu übergeben, ohne eine benannte Komparatorfunktion außerhalb der Funktion definieren zu müssen.)

Ein Ansatz, der manchmal funktioniert, ist die Erfassung von Wert a shared_ptr in einen Heap-allokierten Zustand. Dies wird im Grunde von Hand implementiert, was Python automatisch tut (aber beachte die Referenzzyklen, um Speicherlecks zu vermeiden: Python hat einen Garbage Collector, C ++ nicht).

    
6502 05.01.2015 07:57
quelle
0

Wenn Sie den Rahmen verlassen. Dies wird eine Kopie Ihrer Einheimischen machen.

%Vor%

Wenn Sie im Bereich sind, verwenden Sie besser Referenzen

%Vor%     
user6787837 02.09.2016 13:19
quelle

Tags und Links