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(), [](const auto& a, const auto& b) { return a.size() < b.size(); });
 
  for (auto e : v) std::cout << e << ' ';
  std::cout << '\n';
}
kennen/lambda.txt · Zuletzt geändert: 2019-01-13 12:55 von rrichter