Perché espressione di lambda?
Considera la seguente dichiarazione:
int myint = 52;
Qui, myint è un identificatore, un lValue. 52 è un letterale, un prvalue. Oggi è possibile codificare una funzione in particolare e metterla nella posizione di 52. Tale funzione è chiamata espressione di lambda. Considera anche il seguente breve programma:
#includere
Utilizzo dello spazio dei nomi std;
int fn (int par)
int risposta = par + 3;
risposta di ritorno;
int main ()
fn (5);
restituzione 0;
Oggi è possibile codificare una funzione in particolare e inserirla nella posizione dell'argomento di 5, della chiamata di funzione, FN (5). Tale funzione è chiamata espressione di lambda. L'espressione di lambda (funzione) in quella posizione è un prvalue.
Qualsiasi letterale tranne il letterale della corda è un prvalue. L'espressione di lambda è un design di funzione speciale che si adatterebbe come un codice letterale. È 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 di lambda
Nel seguente programma, una funzione, che è un'espressione Lambda, è assegnata a una variabile:
#includere
Utilizzo dello spazio dei nomi std;
auto fn = [] (int param)
int risposta = param + 3;
risposta di ritorno;
;
int main ()
auto variab = fn (2);
cout << variab << '\n';
restituzione 0;
L'output è:
5Al di fuori della funzione principale (), c'è la variabile, FN. Il suo tipo è auto. Auto in questa situazione significa che il tipo effettivo, come int o float, è determinato dall'operando giusto dell'operatore di assegnazione (=). A destra dell'operatore di assegnazione è un'espressione Lambda. Un'espressione di lambda è una funzione senza il tipo di ritorno precedente. Nota l'uso e la posizione delle staffe quadrate, []. La funzione restituisce 5, un int, che determinerà il tipo per FN.
Nella funzione principale (), c'è l'affermazione:
auto variab = fn (2);
Ciò significa che FN al di fuori di Main (), finisce come identificatore per una funzione. I suoi parametri impliciti sono quelli dell'espressione di lambda. Il tipo per variab è automatico.
Si noti che l'espressione di 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 di 5, è un argomento a un'altra funzione:
#includere
Utilizzo dello spazio dei nomi std;
void Otherfn (int no1, int (*ptr) (int))
int no2 = (*ptr) (2);
cout << no1 << " << no2 << '\n';
int main ()
Otherfn (4, [] (int param)
int risposta = param + 3;
risposta di ritorno;
);
restituzione 0;
L'output è:
4 5Ci sono due funzioni qui, l'espressione di lambda e l'altra Funzione. L'espressione di lambda è il secondo argomento dell'altrofn (), chiamato in main (). Si noti che la funzione Lambda (espressione) non finisce 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, viene utilizzato nella definizione Otherfn () per chiamare la funzione Lambda.
La dichiarazione,
int no2 = (*ptr) (2);
Nella definizione di altrofn (), 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 di funzione di callback C ++.
Parti dell'espressione di lambda
Le parti di una tipica funzione Lambda sono le seguenti:
[] ()
Catture
La definizione della funzione Lambda può essere assegnata a una variabile o utilizzata come argomento a 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 a variabile può anche essere codificata all'interno di un'altra funzione. Se assegnato a una variabile di portata globale, il suo corpo può vedere altre variabili nell'ambito globale. Se assegnato a una variabile all'interno di una definizione di funzione normale, il suo corpo può vedere altre variabili nell'ambito della funzione solo con l'aiuto della clausola di acquisizione, [].
La clausola di cattura [], nota anche come introduttore lambda, consente di inviarsi dalle variabili dall'ambito circostante (funzione) nel corpo della funzione dell'espressione di lambda. Si dice che il corpo della funzione dell'espressione di lambda catturi la variabile quando riceve l'oggetto. Senza la clausola di acquisizione [], una variabile non può essere inviata dall'ambito circostante nel corpo della funzione dell'espressione di lambda. Il seguente programma lo illustra, con l'ambito della funzione principale (), come ambito circostante:
#includere
Utilizzo dello spazio dei nomi std;
int main ()
int id = 5;
auto fn = [id] ()
cout << id << '\n';
;
fn ();
restituzione 0;
L'output è 5. Senza il nome, ID, all'interno [], l'espressione di lambda non avrebbe visto l'ID variabile dell'ambito di funzione principale ().
Cattura per riferimento
L'uso di esempio sopra della clausola di acquisizione sta acquisendo per valore (vedere i dettagli di seguito). Nell'acquisizione per riferimento, la posizione (archiviazione) della variabile, E.G., ID sopra, dell'ambito circostante, è reso disponibile all'interno del corpo della funzione Lambda. Quindi, modificare 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 acquisizione è preceduta da AmperSand (&). Il seguente programma illustra questo:
#includere
Utilizzo dello spazio dei nomi 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';
restituzione 0;
L'output è:
6, 3.4, bConfermare che i nomi delle variabili all'interno del corpo della funzione dell'espressione di lambda sono per le stesse variabili al di fuori dell'espressione di lambda.
Catturare per valore
Nel catturare per valore, una copia della posizione della variabile, dell'ambito circostante, è resa disponibile 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 da ora. Per ottenere l'acquisizione per valore, ogni variabile ripetuta nella clausola di acquisizione non è preceduta da nulla. Il seguente programma illustra questo:
#includere
Utilizzo dello spazio dei nomi 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';
restituzione 0;
L'output è:
5, 2.3, aSe l'indicatore di commento viene rimosso, il programma non si compilerà. Il compilatore emetterà un messaggio di errore secondo cui le variabili all'interno della definizione del corpo della funzione dell'espressione di lambda non possono essere modificate. Sebbene le variabili non possano essere modificate all'interno della funzione Lambda, possono essere cambiate al di fuori della funzione Lambda, come mostra l'output del programma sopra.
Miscelazione delle catture
L'acquisizione per riferimento e l'acquisizione per valore può essere miscelata, come mostra il seguente programma:
#includere
Utilizzo dello spazio dei nomi std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = vero;
auto fn = [id, ft, & ch, & bl] ()
ch = 'b'; bl = false;
cout << id << ", " <<
ft << ", " << ch <<
"", << bl << '\n';
;
fn ();
restituzione 0;
L'output è:
5, 2.3, b, 0Quando tutti catturati, sono per riferimento:
Se tutte le variabili da catturare vengono acquisite per riferimento, allora solo uno e saranno sufficienti nella clausola di cattura. Il seguente programma illustra questo:
#includere
Utilizzo dello spazio dei nomi std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = vero;
auto fn = [&] ()
id = 6; ft = 3.4; ch = 'b'; bl = false;
;
fn ();
cout << id << ", " <<
ft << ", " << ch <<
"", << bl << '\n';
restituzione 0;
L'output è:
6, 3.4, b, 0Se alcune variabili devono essere catturate per riferimento e altre per valore, allora uno e rappresenteranno tutti i riferimenti, e il resto non sarà preceduto da nulla, come mostra il seguente programma:
#includere
Utilizzo dello spazio dei nomi std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = vero;
auto fn = [&, id, ft] ()
ch = 'b'; bl = false;
cout << id << ", " <<
ft << ", " << ch <<
"", << bl << '\n';
;
fn ();
restituzione 0;
L'output è:
5, 2.3, b, 0Nota che e da solo (io.e., 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 acquisizione. Il seguente programma illustra questo:
#includere
Utilizzo dello spazio dei nomi std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = vero;
auto fn = [=] ()
cout << id << ", " <<
ft << ", " << ch <<
"", << bl << '\n';
;
fn ();
restituzione 0;
L'output è:
5, 2.3, a, 1Nota: = è di sola lettura, per ora.
Se alcune variabili devono essere catturate per valore e altre su riferimento, allora uno = rappresenterà tutte le variabili copiate di sola lettura, e il resto ciascuno avrà e, come mostra il seguente programma:
#includere
Utilizzo dello spazio dei nomi std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = vero;
auto fn = [=, & ch, & bl] ()
ch = 'b'; bl = false;
cout << id << ", " << ft <<
"", << ch << ", " <<
bl << '\n';
;
fn ();
restituzione 0;
L'output è:
5, 2.3, b, 0Si noti che = da solo deve essere il primo carattere nella clausola di cattura.
Schema di funzioni di richiamata classica con espressione di lambda
Il seguente programma mostra come uno schema di funzione di callback classico può essere fatto con l'espressione Lambda:
#includere
Utilizzo dello spazio dei nomi std;
char *output;
auto cba = [] (char out [])
output = out;
;
void PrincipalFunc (char input [], void (*pt) (char []))
(*pt) (input);
cout<<"for principal function"<<'\n';
void fn ()
cout<<"Now"<<'\n';
int main ()
char input [] = "per la funzione di callback";
PrincipalFunc (input, CBA);
fn ();
cout<
L'output è:
per la funzione principaleRicordiamo che quando una definizione di espressione di Lambda è assegnata a una variabile nell'ambito globale, il suo corpo funzione può vedere le variabili globali senza utilizzare la clausola di acquisizione.
Il tipo di ritorno finale
Il tipo di ritorno di un'espressione Lambda è automatico, il che significa che il compilatore determina il tipo di ritorno dall'espressione di ritorno (se presente). Se il programmatore vuole davvero indicare il tipo di ritorno, lo farà come nel seguente programma:
#includere
Utilizzo dello spazio dei nomi std;
auto fn = [] (int param) -> int
int risposta = param + 3;
risposta di ritorno;
;
int main ()
auto variab = fn (2);
cout << variab << '\n';
restituzione 0;
L'output è 5. Dopo l'elenco dei parametri, viene digitato l'operatore della freccia. Questo è seguito dal tipo di ritorno (int in questo caso).
Chiusura
Considera il seguente segmento di codice:
struct cla
int id = 5;
char ch = 'a';
obj1, obj2;
Qui, il CLA è il nome della classe Struttura. OBJ1 e OBJ2 sono due oggetti che saranno istanziati dalla classe Struttura. L'espressione di lambda è simile nell'implementazione. La definizione della funzione Lambda è una specie di classe. Quando viene chiamata la funzione Lambda (invocata), un oggetto viene istanziato dalla sua definizione. Questo oggetto è chiamato chiusura. È la chiusura che fa il lavoro che lambda dovrebbe fare.
Tuttavia, codificare l'espressione di lambda come la struttura sopra avrà sostituito OBJ1 e OBJ2 dagli argomenti dei parametri corrispondenti. Il seguente programma illustra questo:
#includere
Utilizzo dello spazio dei nomi std;
auto fn = [] (int param1, int param2)
int risposta = param1 + param2;
risposta di ritorno;
(2, 3);
int main ()
auto var = fn;
cout << var << '\n';
restituzione 0;
L'output è 5. Gli argomenti sono 2 e 3 tra parentesi. Si noti che la chiamata della funzione di espressione di lambda, FN, non subisce alcun argomento poiché gli argomenti sono già stati codificati alla fine della definizione della funzione Lambda.
Conclusione
L'espressione di lambda è una funzione anonima. È in due parti: classe e oggetto. La sua definizione è una specie di classe. Quando viene chiamata l'espressione, un oggetto viene formato dalla definizione. Questo oggetto è chiamato chiusura. È la chiusura che fa il lavoro che lambda dovrebbe fare. Affinché l'espressione di lambda riceva una variabile da un ambito di funzione esterna, ha bisogno di una clausola di cattura non vuota nel suo corpo.