namespace cpp {}

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


kennen:lambda

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';
}
kennen/lambda.txt · Zuletzt geändert: von 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki