Umgebung einfangen
Lambda-Funktionen in C++ beherrschen einen netten Trick, der sie unglaublich viel praktischer macht, als einfache Funktionspointer: sie können Teile ihrer Umgebung “einfangen” und im Funktionsobjekt verpacken. Auf diese Art und Weise kann man Kontext, der zum Zeitpunkt der Erzeugung der Lambda-Funktion bestand, später noch verwenden.
Video
Code
|
|
Erklärung
Die eckigen Klammern, mit denen eine Lambda-Funktion eingeleitet wird, sind nicht für umsonst dort. Sie können die
sogenannte Capture List enthalten: Anweisungen an den Compiler, wie und welche Teile der Umgebung in das resultierende
Funktionsobjekt zu übernehmen sind. Die Funktionen f1
und f2
demonstrieren die zwei gegensätzlichen Varianten:
grundsätzlich die komplette Umgebung steht zur Verfügung (welche Variablen tatsächlich zu übernehmen sind, ergibt sich aus
der Verwendung in der Funktion), einmal als Kopie, einmal als Referenz. Enthält die Liste ein =
, so werden alle
verwendeten Variablen als Kopien in das Funktionsobjekt übernommen. Für die Funktion sichtbar sind als die inhalte der
Umgebung zum Zeitpunkt der Erzeugung des Funktionsobjektes. Ganz anders hingegen die Variante f2
. Enthält die Capture List
ein &
, so wird der gesamte Kontext als Referenzen verfügbar gemacht. Das kann massiv Kopieraufwand sparen (vor allem bei
großen Objekten). Für die Funktion sichtbar werden allerdings die Inhalte der Umgebung zum Zeitpunkt des Aufrufes. Diese
kleine, aber wichtige Unterschied macht die zweite Variante etwas gefährlich: wenn bestimmte Variablen nicht mehr existieren
(weil sie bspw. im Kontext einer Funktion bestanden und nach deren Verlassen aufgeräumt wurden), dann hängen die
betreffenden Referenzen in der Luft. Der Zugriff führt dann im günstigsten Fall zu einem Programmabsturz, im ungünstigsten
zu einer Korruption von fremden Daten. Hier muss man also massiv aufpassen, welche Variante man wirklich braucht und ob im
Falle der Referenzen garantiert werden kann, dass der gesamte Kontext für die Lebensdauer des Funktionsobjektes Bestand hat.
Die letzte Variante f3
ist eine Mischung aus den ersten beiden: x
wird als Referenz, y
als Kopie gefangen. Folgende
Werte werden die Funktionen im Beispiel zu sehen bekommen: f1
sieht 10
und 3
, wie bei der Initialisierung des
Funktionsobjektes bestehend. f2
sieht 5
und 9
, wie sie zum Aufrufzeitpunkt in Zeile 24 gesetzt sind. f3
sieht ein
gemischtes Bild: 5
und 3
, da die Änderung von x
sichtbar wird, die von y
aber nicht.