Lambda-Ausdrücke in C++
In Visual C++ entspricht ein Lambda-Ausdruck - der als Lambda bezeichnet wird - einer anonymen Funktion, deren Zustand aufrecht erhalten wird und mit der auf Variablen, die dem einschließenden Bereich verfügbar sind, zugegriffen werden kann. Dies geschieht, indem eine Klasse definiert wird und ein Objekt dieses Typs erstellt wird. Dieser Artikel definiert, was Lambdas sind, vergleicht sie mit anderen Programmierverfahren, beschreibt ihre Vorteile und bietet ein grundlegendes Beispiel.
Zu Lambdas
Viele Programmiersprachen unterstützen das Konzept einer anonymen Funktion. Das ist eine Funktion mit einem Textkörper aber ohne Namen. Ein Lambda-Ausdruck ist ein mit anonymen Funktionen verknüpftes Programmierverfahren. Mit Lambda wird implizit eine Funktionsobjektklasse definiert und ein Funktionsobjekt dieses Klassentyps erstellt. Weitere Informationen zur Funktionsobjekten finden Sie unter Funktionsobjekte.
Als einführendes Beispiel eines Lambda, zeigt der ISO C++-Standard einen, der im Kontext eines Parameters verwendet wird, der an die std::sort()-Funktion übergeben wird:
#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned n) {
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
} // end of lambda expression
);
}
In diesem Artikel wird erklärt, wie dieser Ausdruck funktioniert.
Wichtig
Lambdas werden in den folgenden verwalteten Entitäten der Common Language Runtime (CLR) nicht unterstützt: ref class, ref struct, value class bzw. value struct.
Funktionsobjekte undLambdas
Sie werden beim Schreiben von Code wahrscheinlich Funktionszeiger und Funktionsobjekte zum Löschen von Problemen und zum Ausführen von Berechnungen verwenden, besonders, wenn Sie mit STL-Algorithmen arbeiten. Funktionszeiger und Funktionsobjekte haben Vorteile und Nachteile – z. B. weisen Funktionszeiger minimalen syntaktischen Mehraufwand auf, behalten aber innerhalb eines Bereichs den Zustand nicht bei. Hingegen können Funktionsobjekte den Zustand zwar beibehalten, erfordern allerdings den syntaktischen Mehraufwand einer Klassendefinition.
Bei einem Lambda werden die Vorteile von Funktionszeigern und Funktionsobjekten kombiniert und deren Nachteile gleichzeitig vermieden. Wie bei Funktionsobjekten ist ein Lambda flexibel und kann den Zustand beibehalten, aber anders als ein Funktionsobjekt benötigt seine kompakte Syntax keine Klassendefinition. Mit der Verwendung von Lambdas können Sie Code schreiben, der weniger schwerfällig und weniger fehleranfällig als Code für ein entsprechendes Funktionsobjekt ist.
In den folgenden Beispielen wird die Verwendung eines Lambda-Ausdrucks mit der Verwendung eines Funktionsobjekts verglichen. Im ersten Beispiel wird ein Lambda-Ausdruck verwendet, um auf der Konsole auszugeben, ob jedes Element in einem vector-Objekt gerade oder ungerade ist. Im zweiten Beispiel wird für die gleiche Aufgabe ein Funktionsobjekt verwendet.
Beispiel 1: Verwendung eines Lambda-Ausdrucks
In diesem Beispiel wird ein im for_each-Funktionsaufruf eingebetteter Lambda-Ausdruck verwendet, um auf der Konsole auszugeben, ob jedes Element in einem vector-Objekt gerade oder ungerade ist.
Code
// even_lambda.cpp
// compile with: cl /EHsc /nologo /W4 /MTd
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// Create a vector object that contains 10 elements.
vector<int> v;
for (int i = 1; i < 10; ++i) {
v.push_back(i);
}
// Count the number of even numbers in the vector by
// using the for_each function and a lambda.
int evenCount = 0;
for_each(v.begin(), v.end(), [&evenCount] (int n) {
cout << n;
if (n % 2 == 0) {
cout << " is even " << endl;
++evenCount;
} else {
cout << " is odd " << endl;
}
});
// Print the count of even numbers to the console.
cout << "There are " << evenCount
<< " even numbers in the vector." << endl;
}
Ausgabe
Kommentare
Im Beispiel handelt es sich beim dritten Argument für die for_each-Funktion um ein Lambda. Mit dem [&evenCount]-Teil wird die Erfassungsklausel des Ausdrucks angegeben. Mit (int n) wird die Parameterliste und mit dem Rest der Text des Ausdrucks angegeben.
Beispiel 2: Verwendung eines Funktionsobjekts
Manchmal wäre die Ausweitung eines Lambdas über den Umfang des vorherigen Beispiels hinaus zu hinderlich. Im folgenden Beispiel wird ein Funktionsobjekt zusammen mit der for_each-Funktion anstelle eines Lambda-Ausdrucks verwendet, um die gleichen Ergebnisse wie in Beispiel 1 zu liefern. In beiden Beispielen wird die Anzahl gerader Zahlen in einem vector-Objekt gespeichert. Um den Zustand des Vorgangs beizubehalten, wird die m_evenCount-Variable von der FunctorClass-Klasse mithilfe eines Verweises als Membervariable gespeichert. Zum Ausführen des Vorgangs wird von FunctorClass der Funktionsaufrufoperator, operator() implementiert. Der Visual C++-Compiler generiert Code, der an Größe und Leistung mit dem in Lambda-Codebeispiel 1 vergleichbar ist. Bei einem grundlegenden Problem, wie dem in diesem Artikel, ist der einfachere Lambda-Entwurf wahrscheinlich besser geeignet, als der Funktionsobjektentwurf. Wenn Sie allerdings der Meinung sind, die Funktionalität erfordere zukünftig möglicherweise beträchtliche Erweiterungen, verwenden Sie einen Funktionsobjektentwurf. Damit ist die Codeverwaltung einfacher.
Weitere Informationen zum operator() finden Sie unter Funktionsaufruf (C++). Weitere Informationen zur for_each-Funktion finden Sie unter for_each.
Code
// even_functor.cpp
// compile with: /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class FunctorClass
{
public:
// The required constructor for this example.
explicit FunctorClass(int& evenCount)
: m_evenCount(evenCount) { }
// The function-call operator prints whether the number is
// even or odd. If the number is even, this method updates
// the counter.
void operator()(int n) const {
cout << n;
if (n % 2 == 0) {
cout << " is even " << endl;
++m_evenCount;
} else {
cout << " is odd " << endl;
}
}
private:
// Default assignment operator to silence warning C4512.
FunctorClass& operator=(const FunctorClass&);
int& m_evenCount; // the number of even variables in the vector.
};
int main()
{
// Create a vector object that contains 10 elements.
vector<int> v;
for (int i = 1; i < 10; ++i) {
v.push_back(i);
}
// Count the number of even numbers in the vector by
// using the for_each function and a function object.
int evenCount = 0;
for_each(v.begin(), v.end(), FunctorClass(evenCount));
// Print the count of even numbers to the console.
cout << "There are " << evenCount
<< " even numbers in the vector." << endl;
}
Ausgabe
Zusammenfassung
Lambdas sind eine leistungsstarke und ausdrucksvolle Programmiertechnik. Mehr zu den Teilen und Eigenschaften eines Lambda-Ausdrucks erfahren Sie unter Lambda-Ausdruckssyntax. Wie Sie Lambdas in Programmen verwenden erfahren Sie unter Beispiele für Lambda-Ausdrücke.