Perché l'espressione Lambda??
Considera la seguente affermazione:
int mioInt = 52;Qui, myInt è un identificatore, un lvalue. 52 è un letterale, un valore. Oggi è possibile codificare una funzione appositamente e metterla nella posizione di 52. Tale funzione è chiamata espressione lambda. Considera anche il seguente breve programma:
#includereusando lo spazio dei nomi std;
int fn(int par)
int risposta = par + 3;
risposta di ritorno;
intero principale()
fn(5);
restituisce 0;
Oggi è possibile codificare una funzione appositamente e metterla nella posizione dell'argomento di 5, della chiamata di funzione, fn(5). Tale funzione è chiamata espressione lambda. L'espressione lambda (funzione) in quella posizione è un prvalue.
Qualsiasi letterale tranne il letterale stringa è un prvalue. L'espressione lambda è un design di funzione speciale che si adatterebbe come un letterale nel codice. È una funzione anonima (senza nome). Questo articolo spiega la nuova espressione primaria C++, chiamata espressione lambda. La conoscenza di base in C++ è un requisito per comprendere questo articolo.
Contenuto dell'articolo
- Illustrazione dell'espressione Lambda
- Parti dell'espressione Lambda
- cattura
- Schema di funzione di callback classico con espressione lambda
- Il tipo trailing-return
- Chiusura
- Conclusione
Illustrazione dell'espressione Lambda
Nel seguente programma, una funzione, che è un'espressione lambda, viene assegnata a una variabile:
#includereusando lo spazio dei nomi std;
auto fn = [](int param)
int risposta = parametro + 3;
risposta di ritorno;
;
intero principale()
variabile automatica = fn(2);
cout << variab << '\n';
restituisce 0;
L'uscita è:
5Al di fuori della funzione main(), c'è la variabile, fn. Il suo tipo è auto. Auto in questa situazione significa che il tipo effettivo, come int o float, è determinato dall'operando destro dell'operatore di assegnazione (=). A destra dell'operatore di assegnazione c'è un'espressione lambda. Un'espressione lambda è una funzione senza il tipo restituito precedente. Notare l'uso e la posizione delle parentesi quadre, []. La funzione restituisce 5, un int, che determinerà il tipo per fn.
Nella funzione main(), c'è l'istruzione:
variabile automatica = fn(2);Ciò significa che, al di fuori di main(), finisce per essere l'identificatore di una funzione. I suoi parametri impliciti sono quelli dell'espressione lambda. Il tipo di variabile è auto.
Nota che l'espressione lambda termina con un punto e virgola, proprio come la definizione di classe o struct, termina con un punto e virgola.
Nel seguente programma, una funzione, che è un'espressione lambda che restituisce il valore 5, è un argomento per un'altra funzione:
#includereusando lo spazio dei nomi std;
void altrofn (int no1, int (*ptr)(int))
int no2 = (*ptr)(2);
cout << no1 << " << no2 << '\n';
intero principale()
altrofn(4, [](int param)
int risposta = parametro + 3;
risposta di ritorno;
);
restituisce 0;
L'uscita è:
4 5Ci sono due funzioni qui, l'espressione lambda e la funzione otherfn(). L'espressione lambda è il secondo argomento di otherfn(), chiamato in main(). Nota che la funzione lambda (espressione) non termina con un punto e virgola in questa chiamata perché, qui, è un argomento (non una funzione autonoma).
Il parametro della funzione lambda nella definizione della funzione otherfn() è un puntatore a una funzione. Il puntatore ha il nome, ptr. Il nome, ptr, è usato nella definizione otherfn() per chiamare la funzione lambda.
La dichiarazione,
int no2 = (*ptr)(2);Nella definizione otherfn(), chiama la funzione lambda con un argomento di 2. Il valore di ritorno della chiamata, "(*ptr)(2)" dalla funzione lambda, è assegnato a no2.
Il programma sopra mostra anche come la funzione lambda può essere utilizzata nello schema della funzione di callback C++.
Parti dell'espressione Lambda
Le parti di una tipica funzione lambda sono le seguenti:
[] ()- [] è la clausola di cattura. Può avere oggetti.
- () è per l'elenco dei parametri.
- è per il corpo della funzione. Se la funzione è isolata, dovrebbe terminare con un punto e virgola.
cattura
La definizione della funzione lambda può essere assegnata a una variabile o utilizzata come argomento per una chiamata di funzione diversa. La definizione per tale chiamata di funzione dovrebbe avere come parametro, un puntatore a una funzione, corrispondente alla definizione della funzione lambda.
La definizione della funzione lambda è diversa dalla definizione della funzione normale. Può essere assegnato a una variabile nell'ambito globale; questa funzione assegnata alla variabile può anche essere codificata all'interno di un'altra funzione. Quando assegnato a una variabile di ambito globale, il suo corpo può vedere altre variabili nell'ambito globale. Quando viene assegnata a una variabile all'interno di una normale definizione di funzione, il suo corpo può vedere altre variabili nell'ambito della funzione solo con l'aiuto della clausola di cattura, [].
La clausola di cattura [], nota anche come lambda-introducer, consente di inviare variabili dall'ambito (funzione) circostante al corpo della funzione dell'espressione lambda. Si dice che il corpo della funzione dell'espressione lambda catturi la variabile quando riceve l'oggetto. Senza la clausola di cattura [], non è possibile inviare una variabile dall'ambito circostante al corpo della funzione dell'espressione lambda. Il seguente programma illustra questo, con l'ambito della funzione main(), come ambito circostante:
#includereusando lo spazio dei nomi std;
intero principale()
int id = 5;
auto fn = [id]()
cout << id << '\n';
;
fn();
restituisce 0;
L'uscita è 5. Senza il nome, id, all'interno di [], l'espressione lambda non avrebbe visto l'id variabile dell'ambito della funzione main().
Acquisizione per riferimento
L'uso dell'esempio sopra della clausola di acquisizione è l'acquisizione per valore (vedi i dettagli di seguito). Nell'acquisizione per riferimento, la posizione (memorizzazione) della variabile, e.g., id sopra, dell'ambito circostante, è reso disponibile all'interno del corpo della funzione lambda. Quindi, cambiando il valore della variabile all'interno del corpo della funzione lambda cambierà il valore di quella stessa variabile nell'ambito circostante. Ogni variabile ripetuta nella clausola di cattura è preceduta dalla e commerciale (&) per ottenere ciò. Il seguente programma lo illustra:
#includereusando lo spazio dei nomi std;
intero principale()
int id = 5; galleggiante ft = 2.3; char ch = 'A';
auto fn = [&id, &ft, &ch]()
ID = 6; piedi = 3.4; ch = 'B';
;
fn();
cout << id << ", " << ft << ", " << ch << '\n';
restituisce 0;
L'uscita è:
6, 3.4, BConfermando che i nomi delle variabili all'interno del corpo della funzione dell'espressione lambda sono per le stesse variabili al di fuori dell'espressione lambda.
Cattura per valore
Nell'acquisizione per valore, viene resa disponibile una copia della posizione della variabile, dell'ambito circostante, all'interno del corpo della funzione lambda. Sebbene la variabile all'interno del corpo della funzione lambda sia una copia, il suo valore non può essere modificato all'interno del corpo al momento. Per ottenere la cattura per valore, ogni variabile ripetuta nella clausola di cattura non è preceduta da nulla. Il seguente programma lo illustra:
#includereusando lo spazio dei nomi std;
intero principale()
int id = 5; galleggiante ft = 2.3; char ch = 'A';
auto fn = [id, ft, ch]()
//id = 6; piedi = 3.4; ch = 'B';
cout << id << ", " << ft << ", " << ch << '\n';
;
fn();
ID = 6; piedi = 3.4; ch = 'B';
cout << id << ", " << ft << ", " << ch << '\n';
restituisce 0;
L'uscita è:
5, 2.3, A6, 3.4, B
Se l'indicatore di commento viene rimosso, il programma non verrà compilato. Il compilatore emetterà un messaggio di errore che le variabili all'interno della definizione del corpo della funzione dell'espressione lambda non possono essere modificate. Sebbene le variabili non possano essere modificate all'interno della funzione lambda, possono essere modificate al di fuori della funzione lambda, come mostra l'output del programma sopra.
Catture di mixaggio
L'acquisizione per riferimento e l'acquisizione per valore possono essere combinate, come mostra il seguente programma:
#includereusando lo spazio dei nomi std;
intero principale()
int id = 5; galleggiante ft = 2.3; char ch = 'A'; bool bl = vero;
auto fn = [id, ft, &ch, &bl]()
ch = 'B'; bl = falso;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
restituisce 0;
L'uscita è:
5, 2.3, SI, 0Quando tutti catturati, sono per riferimento:
Se tutte le variabili da catturare vengono catturate per riferimento, allora solo una & sarà sufficiente nella clausola di cattura. Il seguente programma lo illustra:
#includereusando lo spazio dei nomi std;
intero principale()
int id = 5; galleggiante ft = 2.3; char ch = 'A'; bool bl = vero;
auto fn = [&]()
ID = 6; piedi = 3.4; ch = 'B'; bl = falso;
;
fn();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
restituisce 0;
L'uscita è:
6, 3.4, SI, 0Se alcune variabili devono essere catturate per riferimento e altre per valore, allora uno & rappresenterà tutti i riferimenti e il resto non sarà preceduto da nulla, come mostra il seguente programma:
usando lo spazio dei nomi std;intero principale()
int id = 5; galleggiante ft = 2.3; char ch = 'A'; bool bl = vero;
auto fn = [&, id, piedi]()
ch = 'B'; bl = falso;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
restituisce 0;
L'uscita è:
5, 2.3, SI, 0Nota che & da solo (i.e., & non seguito da un identificatore) deve essere il primo carattere nella clausola di cattura.
Quando tutti catturati, sono per valore:
Se tutte le variabili da catturare devono essere catturate per valore, allora solo uno = sarà sufficiente nella clausola di cattura. Il seguente programma lo illustra:
#includereusando lo spazio dei nomi std;
intero principale()
int id = 5; galleggiante ft = 2.3; char ch = 'A'; bool bl = vero;
auto fn = [=]()
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
restituisce 0;
L'uscita è:
5, 2.3, LA, 1Nota: = è di sola lettura, al momento.
Se alcune variabili devono essere catturate per valore e altre per riferimento, allora una = rappresenterà tutte le variabili copiate di sola lettura e le altre avranno ciascuna &, come mostra il seguente programma:
#includereusando lo spazio dei nomi std;
intero principale()
int id = 5; galleggiante ft = 2.3; char ch = 'A'; bool bl = vero;
auto fn = [=, &ch, &bl]()
ch = 'B'; bl = falso;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
restituisce 0;
L'uscita è:
5, 2.3, SI, 0Nota che = solo deve essere il primo carattere nella clausola di cattura.
Schema di funzione di callback classico con espressione lambda
Il seguente programma mostra come è possibile eseguire uno schema di funzione di callback classico con l'espressione lambda:
#includereusando lo spazio dei nomi std;
carattere *output;
auto cba = [](char out[])
uscita = uscita;
;
void principalFunc(char input[], void (*pt)(char[]))
(*pt)(ingresso);
cout<<"for principal function"<<'\n';
vuoto fn()
cout<<"Now"<<'\n';
intero principale()
char input[] = "per la funzione di callback";
principalFunc(input, cba);
fn();
cout<