Lambda -Ausdrücke in C ++

Lambda -Ausdrücke in C ++

Warum Lambda -Ausdruck?

Betrachten Sie die folgende Erklärung:

int myint = 52;

Hier ist Myint ein Kennung, ein Lvalue. 52 ist ein buchstäblicher, ein Prvalue. Heute ist es möglich, eine Funktion speziell zu codieren und in die Position von 52 zu setzen. Eine solche Funktion wird als Lambda -Ausdruck bezeichnet. Betrachten Sie auch das folgende kurze Programm:

#enthalten
Verwenden von Namespace STD;
int fn (int par)

int beantwortet = par + 3;
Antwort zurück;

int main ()

fn (5);
Rückkehr 0;

Heute ist es möglich, eine Funktion speziell zu codieren und sie in die Position des Arguments von 5 des Funktionsaufrufs Fn (5) zu setzen. Eine solche Funktion wird als Lambda -Ausdruck bezeichnet. Der Lambda -Ausdruck (Funktion) in dieser Position ist ein Prorue.

Jeder buchstäbliche außer dem Streichliteral ist ein Prvalue. Der Lambda -Ausdruck ist ein spezielles Funktionsdesign, das als wörtlicher Code passen würde. Es ist eine anonyme (unbenannte) Funktion. Dieser Artikel erklärt den neuen C ++ - primären Ausdruck, der als Lambda -Ausdruck bezeichnet wird. Grundkenntnisse in C ++ sind eine Voraussetzung, um diesen Artikel zu verstehen.

Artikelinhalt

  • Illustration des Lambda -Ausdrucks
  • Teile des Lambda -Ausdrucks
  • Erfasst
  • Klassisches Rückruffunktionsschema mit Lambda -Ausdruck
  • Der nachgefolgte Return-Typ
  • Schließung
  • Abschluss

Illustration des Lambda -Ausdrucks

Im folgenden Programm wird eine Funktion, die ein Lambda -Ausdruck ist, einer Variablen zugeordnet:

#enthalten
Verwenden von Namespace STD;
auto fn = [] (int param)

int beantwortet = param + 3;
Antwort zurück;
;
int main ()

Auto variab = fn (2);
Cout << variab << '\n';
Rückkehr 0;

Die Ausgabe ist:

5

Außerhalb der Funktion main () gibt es die Variable Fn. Sein Typ ist automatisch. Auto in dieser Situation bedeutet, dass der tatsächliche Typ wie int oder float durch den rechten Operanden des Zuordnungsoperators bestimmt wird (=). Rechts vom Zuordnungsoperator befindet sich ein Lambda -Ausdruck. Ein Lambda -Ausdruck ist eine Funktion ohne den vorhergehenden Rückgabetyp. Beachten Sie die Verwendung und Position der quadratischen Klammern []. Die Funktion gibt 5, eine INT zurück, die den Typ für FN bestimmt.

In der Funktion main () gibt es die Anweisung:

Auto variab = fn (2);

Dies bedeutet, dass FN außerhalb main () als Bezeichner für eine Funktion endet. Seine impliziten Parameter sind die des Lambda -Ausdrucks. Der Typ für Variab ist automatisch.

Beachten Sie, dass der Lambda -Ausdruck mit einem Semikolon endet, genau wie die Klassen- oder Strukturdefinition, mit einem Semikolon endet.

Im folgenden Programm ist eine Funktion, bei der es sich um einen Lambda -Ausdruck handelt, der den Wert von 5 zurückgibt, ein Argument für eine andere Funktion:

#enthalten
Verwenden von Namespace STD;
void otherfn (int no1, int (*ptr) (int))

int no2 = (*ptr) (2);
Cout << no1 << " << no2 << '\n';

int main ()

Andere fn (4, [] (int Param)

int beantwortet = param + 3;
Antwort zurück;
);
Rückkehr 0;

Die Ausgabe ist:

4 5

Hier gibt es zwei Funktionen. Der Lambda -Ausdruck ist das zweite Argument der anderen Fn (), die in Main () genannt wird. Beachten Sie, dass die Lambda-Funktion (Ausdruck) in diesem Aufruf nicht mit einem Semikolon endet, da es sich hier um ein Argument handelt (keine eigenständige Funktion).

Der Lambda -Funktionsparameter in der Definition der anderen Funktion der anderen Fn () ist ein Zeiger auf eine Funktion. Der Zeiger hat den Namen, PTR. Der Name PTR wird in der ONDERENFN () -Definition verwendet, um die Lambda -Funktion aufzurufen.

Die Aussage,

int no2 = (*ptr) (2);

In der ONDERENFN () Definition ruft es die Lambda -Funktion mit einem Argument von 2 auf. Der Rückgabewert des Anrufs "(*ptr) (2)" von der Lambda -Funktion wird NO2 zugewiesen.

Das obige Programm zeigt auch, wie die Lambda -Funktion im C ++ - Callback -Funktionsschema verwendet werden kann.

Teile des Lambda -Ausdrucks

Die Teile einer typischen Lambda -Funktion sind wie folgt:

[] ()
  • [] ist die Erfassungsklausel. Es kann Gegenstände haben.
  • () ist für die Parameterliste.
  • ist für den Funktionskörper. Wenn die Funktion alleine steht, sollte sie mit einem Semikolon enden.

Erfasst

Die Lambda -Funktionsdefinition kann einer Variablen zugeordnet oder als Argument für einen anderen Funktionsaufruf verwendet werden. Die Definition für einen solchen Funktionsaufruf sollte als Parameter, einen Zeiger auf eine Funktion haben, der der Lambda -Funktionsdefinition entspricht.

Die Lambda -Funktionsdefinition unterscheidet sich von der normalen Funktionsdefinition. Es kann einer Variablen im globalen Bereich zugeordnet werden. Diese funktionsgestützte bis variable kann auch in einer anderen Funktion codiert werden. Wenn er einer globalen Umfangsvariablen zugeordnet ist, kann sein Körper andere Variablen im globalen Bereich sehen. Wenn er einer Variablen innerhalb einer normalen Funktionsdefinition zugeordnet ist, kann sein Körper andere Variablen im Funktionsbereich nur mit der Hilfe der Erfassungsklausel [] sehen [].

Die Erfassungsklausel [], auch als Lambda-In-Autoducer bezeichnet, ermöglicht es Variablen, die aus dem Umgebungsbereich (Funktion) in die Funktion des Lambda-Expression gesendet werden. Der Funktionskörper des Lambda -Ausdrucks soll die Variable erfassen, wenn er das Objekt empfängt. Ohne die Erfassungsklausel [] kann eine Variable nicht aus dem umgebenden Bereich in die Funktion des Lambda -Ausdrucks gesendet werden. Das folgende Programm veranschaulicht dies mit dem Main () -Funktionsumfang als umgebender Bereich:

#enthalten
Verwenden von Namespace STD;
int main ()

int id = 5;
Auto fn = [id] ()

Cout << id << '\n';
;
fn ();
Rückkehr 0;

Die Ausgabe ist 5. Ohne den Namen, ID, in [] hätte der Lambda -Ausdruck die variable ID des Funktionsbereichs main () nicht gesehen.

Erfassen durch Bezugnahme

Die obige Beispiel Verwendung der Erfassungsklausel erfasst nach Wert (siehe Details unten). Bei der Erfassung durch Bezugnahme, der Ort (Speicher) der Variablen, e.G., ID oben des umgebenden Bereichs wird innerhalb der Lambda -Funktionskörper verfügbar gemacht. Wenn Sie den Wert der Variablen innerhalb des Lambda -Funktionsgremiums ändern. Jede Variable, die in der Erfassungsklausel wiederholt wird. Das folgende Programm zeigt dies:

#enthalten
Verwenden von Namespace STD;
int main ()

int id = 5; float ft = 2.3; char ch = 'a';
Auto fn = [& id, & ft, & ch] ())

id = 6; ft = 3.4; ch = 'b';
;
fn ();
Cout << id << ", " << ft << ", " << ch << '\n';
Rückkehr 0;

Die Ausgabe ist:

6, 3.4, b

Bestätigen, dass die Variablennamen innerhalb des Funktionskörpers des Lambda -Ausdrucks für dieselben Variablen außerhalb des Lambda -Ausdrucks gelten.

Nach Wert erfassen

Bei der Erfassung nach Wert wird eine Kopie des Standorts der Variablen des umgebenden Bereichs in der Lambda -Funktionsbehörde zur Verfügung gestellt. Obwohl die Variable innerhalb der Lambda -Funktionskörper eine Kopie ist, kann ihr Wert derzeit nicht im Körper geändert werden. Um das Erfassen nach Wert zu erreichen, wird jeder in der Erfassungsklausel wiederholten Variablen nichts vorausgehen. Das folgende Programm zeigt dies:

#enthalten
Verwenden von Namespace STD;
int main ()

int id = 5; float ft = 2.3; char ch = 'a';
auto fn = [id, ft, ch] ())

// id = 6; ft = 3.4; ch = 'b';
Cout << id << ", " << ft << ", " << ch << '\n';
;
fn ();
id = 6; ft = 3.4; ch = 'b';
Cout << id << ", " << ft << ", " << ch << '\n';
Rückkehr 0;

Die Ausgabe ist:

5, 2.3, a
6, 3.4, b

Wenn der Kommentarindikator entfernt wird, wird das Programm nicht kompiliert. Der Compiler gibt eine Fehlermeldung aus, dass die Variablen in der Definition des Funktionskörpers des Lambda -Ausdrucks nicht geändert werden können. Obwohl die Variablen innerhalb der Lambda -Funktion nicht geändert werden können, können sie außerhalb der Lambda -Funktion geändert werden, wie die Ausgabe des obigen Programms zeigt.

Mischen erfassen

Das Erfassen durch Referenz und Erfassung durch Wert kann gemischt werden, wie das folgende Programm zeigt:

#enthalten
Verwenden von Namespace STD;
int main ()

int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
auto fn = [id, ft & ch, & bl] ()

ch = 'b'; bl = false;
Cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
Rückkehr 0;

Die Ausgabe ist:

5, 2.3, b, 0

Wenn alle erfasst werden, werden nach Bezugnahme:

Wenn alle zu erfassten Variablen durch Bezugnahme erfasst werden, genügt nur eine und reicht in der Erfassungsklausel aus. Das folgende Programm zeigt dies:

#enthalten
Verwenden von Namespace STD;
int main ()

int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
Auto fn = [&] ()

id = 6; ft = 3.4; ch = 'b'; bl = false;
;
fn ();
Cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
Rückkehr 0;

Die Ausgabe ist:

6, 3.4, b, 0

Wenn einige Variablen durch Referenz und andere nach Wert erfasst werden sollen, wird eine und alle Referenzen dargestellt, und der Rest wird jeweils nichts vorgehen, wie das folgende Programm zeigt:

#enthalten
Verwenden von Namespace STD;
int main ()

int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
auto fn = [&, id, ft] ())

ch = 'b'; bl = false;
Cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
Rückkehr 0;

Die Ausgabe ist:

5, 2.3, b, 0

Beachten Sie das und allein (ich.e., & nicht gefolgt von einer Kennung) muss der erste Charakter in der Erfassungsklausel sein.

Wenn alle erfasst werden, sind sie nach Wert:

Wenn alle zu erfassten Variablen von Wert erfasst werden sollen, reicht nur einer = in der Erfassungsklausel aus. Das folgende Programm zeigt dies:

#enthalten
Verwenden von Namespace STD;
int main ()

int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
Auto fn = [=] ()

Cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
Rückkehr 0;

Die Ausgabe ist:

5, 2.3, a, 1

Notiz: = ist ab sofort schreibgeschützt.

Wenn einige Variablen durch Wert und andere durch Bezugnahme erfasst werden sollen, wird eine = alle schreibgeschützten kopierten Variablen dargestellt, und der Rest hat jeweils und, wie das folgende Programm zeigt:

#enthalten
Verwenden von Namespace STD;
int main ()

int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
Auto fn = [=, & ch, & bl] ())

ch = 'b'; bl = false;
Cout << id << ", " << ft <<
"," << ch << ", " <<
bl << '\n';
;
fn ();
Rückkehr 0;

Die Ausgabe ist:

5, 2.3, b, 0

Beachten Sie, dass = allein das erste Zeichen in der Erfassungsklausel sein muss.

Klassisches Rückruffunktionsschema mit Lambda -Ausdruck

Das folgende Programm zeigt, wie ein klassischer Rückruffunktionsfunktionsschema mit dem Lambda -Ausdruck durchgeführt werden kann:

#enthalten
Verwenden von Namespace STD;
char *output;
Auto CBA = [] (char out [])

output = out;
;
void PrincipalFunc (char input [], void (*pt) (char []))

(*pt) (Eingabe);
Cout<<"for principal function"<<'\n';

void fn ()

Cout<<"Now"<<'\n';

int main ()

char input [] = "Für die Rückruffunktion";
PrincipalFunc (Eingabe, CBA);
fn ();
Cout<Rückkehr 0;

Die Ausgabe ist:

Für die Hauptfunktion
Jetzt
Für Rückruffunktion

Erinnern Sie sich daran, dass eine Variable im globalen Bereich zugewiesen wird, wenn eine Lambda -Expressionsdefinition zugeordnet ist, ihre Funktionsbehörde globale Variablen sehen kann, ohne die Erfassungsklausel zu verwenden.

Der nachgefolgte Return-Typ

Der Rückgabetyp eines Lambda -Ausdrucks ist automatisch, was bedeutet, dass der Compiler den Rückgabetyp aus dem Rückgabeausdruck bestimmt (falls vorhanden). Wenn der Programmierer wirklich den Rückgabetyp angeben möchte, wird er dies wie im folgenden Programm tun:

#enthalten
Verwenden von Namespace STD;
auto fn = [] (int param) -> int

int beantwortet = param + 3;
Antwort zurück;
;
int main ()

Auto variab = fn (2);
Cout << variab << '\n';
Rückkehr 0;

Der Ausgang ist 5. Nach der Parameterliste wird der Pfeiloperator eingegeben. Darauf folgt der Rückgabetyp (int in diesem Fall).

Schließung

Betrachten Sie das folgende Codesegment:

Struktur Cla

int id = 5;
char ch = 'a';
obj1, obj2;

Hier ist CLA der Name der Strukturklasse. OBJ1 und OBJ2 sind zwei Objekte, die aus der Struct -Klasse instanziiert werden. Die Lambda -Expression ist in der Implementierung ähnlich. Die Lambda -Funktionsdefinition ist eine Art Klasse. Wenn die Lambda -Funktion aufgerufen wird (aufgerufen), wird ein Objekt aus seiner Definition instanziiert. Dieses Objekt wird als Schließung bezeichnet. Es ist die Schließung, die die Arbeit, die die Lambda erledigt.

Die Codierung des Lambda -Ausdrucks wie die obige Struktur wird jedoch OBJ1 und OBJ2 durch die entsprechenden Parameter -Argumente ersetzt. Das folgende Programm zeigt dies:

#enthalten
Verwenden von Namespace STD;
auto fn = [] (int param1, int param2)

int beantwortet = param1 + param2;
Antwort zurück;
(2, 3);
int main ()

Auto var = fn;
Cout << var << '\n';
Rückkehr 0;

Der Ausgang ist 5. Die Argumente sind 2 und 3 in Klammern. Beachten Sie, dass der Aufruf der Lambda -Expressionsfunktion FN kein Argument nimmt, da die Argumente bereits am Ende der Lambda -Funktionsdefinition codiert wurden.

Abschluss

Der Lambda -Ausdruck ist eine anonyme Funktion. Es ist in zwei Teilen: Klasse und Objekt. Seine Definition ist eine Art Klasse. Wenn der Ausdruck aufgerufen wird, wird aus der Definition ein Objekt gebildet. Dieses Objekt wird als Schließung bezeichnet. Es ist die Schließung, die die Arbeit, die die Lambda erledigt. Damit der Lambda-Ausdruck eine Variable aus einem äußeren Funktionsbereich empfangen kann.