Inhaltsverzeichnis
Lambda-Ausdrücke
// Aus diesem Grunde kratzen die folgenden Beispiele nur an der Oberfläche. //
// Andererseits können wir nur an der Oberfläche kratzen, denn wenn wir tiefer kratzen, sind wir nicht mehr an der Oberfläche. //— Lawrence J. Peter
Ad-hoc-Funktionen
Algorithmen der Standard-Bibliothek wie for_each() erwarten oft eine Funktion oder einen Funktor, der auf Elemente eines Bereichs angewendet wird. Mit einem Lambda-Ausdruck kann diese Funktion an Ort und Stelle erzeugt werden:
#include <algorithm> #include <iostream> #include <string> void lambdademo() { std::string s = "Hello, Lambda Expressions in C++11!"; std::for_each(s.begin(), s.end(), [] (char c) { std::cout << c; } // <-- hier ); std::cout << std::endl; }
Lambdas sind anonyme, lokal definierte Funktionen.
Der Rückgabetyp wird, sofern möglich, automatisch ermittelt.
Er kann hinter der Parameterliste mit -> Zieltyp angegeben werden.
std::string uppercase; std::copy_if(s.begin(), s.end(), std::back_inserter(uppercase), [] (char c) -> bool { return ('A' <= c && c <= 'Z'); } ); std::cout << uppercase << std::endl;
Man beachte den neuen Algorithmus copy_if() in <algorithm>.
Lambdas sind als Variablenwerte speicherbar, um sie mehrfach zu nutzen. Das Schlüsselwort auto ist hierbei hilfreich:
auto fromAtoZ = [] (char c) -> char { return ('A' <= c && c <= 'Z') ? c : '.'; }; std::list<char> aList; std::transform(s.begin(), s.end(), std::back_inserter(aList), fromAtoZ);
Closure
Lambda-Ausdrücke beginnen mit einer eckigen Klammer. In dieser kann angegeben werden, welche lokalen Variablen(werte) der Umgebung innerhalb des Lambda-Ausdrucks bekannt sein sollen. Dieses Einbeziehen der Umgebung wird Closure (Einschluss) genannt.
#include <algorithm> #include <iostream> #include <vector> #include <string> void closuredemo() { std::string s = "Hello, lambda expressions in C++11!"; char low = 'A', high = 'Z'; std::for_each(s.begin(), s.end(), [low, high] (char c) // closure by value { if (low <= c && c <= high) std::cout << c; } ); std::cout << std::endl; }
Hier werden die bei der Lambda-Definition aktuellen Werte der Variablen low und high übernommen.
Auch referenzieller Einschluss ist möglich und manchmal erwünscht:
int count = 0; std::for_each(s.begin(), s.end(), [low, high, &count] (char c) // closure by reference: &count { if (low <= c && c <= high) ++count; } ); std::cout << count << std::endl;
Durch die Angabe [&] werden alle Variablen der Umgebung als Referenz übernommen.
Durch [=] werden alle Werte der Umgebung kopiert.
Auch gemischte Angaben [=, &count] sind möglich.
In Methoden definierte Lambdas können [this] (als Wert) einschließen,
um an Attribute eines Objektes heranzukommen.
Weitere Beispiele sind im
Visual Team Blog
zu finden.
Lambda-capture-Ausdrücke
C++14 erlaubt bei der Definition des Lambda-Ausdrucks neue lokale Variablen einzuführen, die ihre Werte durch Ausdrücke erhalten:
void tabulate(double a, double b, int n) { auto f = [a, dy = (b-a)/n] (int i) { return a + i * dy; }; for (int i = 0; i <= n; ++i) { std::cout << i << '\t' << f(i) << '\n'; } }
Generische Lambda-Ausdrücke
Der Typ von Lambda-Parametern kann vom Compiler ermittelt werden (ab C++14):
void generic_demo() { std::vector<std::string> v { "generic", "lambdas", "in", "C++14" }; std::sort(v.begin(), v.end(), [](auto a, auto b) { return a.size() < b.size(); }); for (auto e : v) std::cout << e << ' '; std::cout << '\n'; }
