C++

Espressioni lambda in C++

Espressioni lambda in C++

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:

#includere
usando 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:

#includere
usando 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 è:

    5

Al 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:

#includere
usando 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  5

Ci 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:

#includere
usando 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:

#includere
usando 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, B

Confermando 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:

#includere
usando 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, A
6, 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:

#includere
usando 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, 0

Quando 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:

#includere
usando 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, 0

Se 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, 0

Nota 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:

#includere
usando 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, 1

Nota: = è 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:

#includere
usando 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, 0

Nota 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:

#includere
usando 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<restituisce 0;

L'uscita è:

per la funzione principale
Adesso
per la funzione di richiamata

Ricorda che quando una definizione di espressione lambda viene assegnata a una variabile nell'ambito globale, il suo corpo funzione può vedere le variabili globali senza utilizzare la clausola di cattura.

Il tipo trailing-return

Il tipo restituito di un'espressione lambda è auto, il che significa che il compilatore determina il tipo restituito dall'espressione restituita (se presente). Se il programmatore vuole davvero indicare il tipo di ritorno, allora lo farà come nel seguente programma:

#includere
usando lo spazio dei nomi std;
auto fn = [](int param) -> int

int risposta = parametro + 3;
risposta di ritorno;
;
intero principale()

variabile automatica = fn(2);
cout << variab << '\n';
restituisce 0;

L'uscita è 5. Dopo l'elenco dei parametri, viene digitato l'operatore freccia. Questo è seguito dal tipo restituito (int in questo caso).

Chiusura

Considera il seguente segmento di codice:

struttura Cla

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

Qui, Cla è il nome della classe struct.  Obj1 e obj2 sono due oggetti che verranno istanziati dalla classe struct. L'espressione lambda è simile nell'implementazione. La definizione della funzione lambda è una specie di classe. Quando la funzione lambda viene chiamata (invocata), un oggetto viene istanziato dalla sua definizione. Questo oggetto è chiamato chiusura. È la chiusura che fa il lavoro che dovrebbe fare la lambda.

Tuttavia, codificando l'espressione lambda come la struttura sopra, obj1 e obj2 saranno sostituiti dagli argomenti dei parametri corrispondenti. Il seguente programma lo illustra:

#includere
usando lo spazio dei nomi std;
auto fn = [](int param1, int param2)

int risposta = param1 + param2;
risposta di ritorno;
(2, 3);
intero principale()

auto var = fn;
cout << var << '\n';
restituisce 0;

L'uscita è 5. Gli argomenti sono 2 e 3 tra parentesi. Si noti che la chiamata alla funzione dell'espressione lambda, fn, non accetta alcun argomento poiché gli argomenti sono già stati codificati alla fine della definizione della funzione lambda.

Conclusione

L'espressione lambda è una funzione anonima. È in due parti: classe e oggetto. La sua definizione è una specie di classe. Quando viene chiamata l'espressione, si forma un oggetto dalla definizione. Questo oggetto è chiamato chiusura. È la chiusura che fa il lavoro che dovrebbe fare la lambda.

Affinché l'espressione lambda riceva una variabile da un ambito di funzione esterno, è necessaria una clausola di acquisizione non vuota nel corpo della funzione.

Il tasto sinistro del mouse non funziona su Windows 10
Se stai utilizzando un mouse dedicato con il tuo laptop o computer desktop ma il il tasto sinistro del mouse non funziona su Windows 10/8/7 per qualch...
Il cursore salta o si sposta in modo casuale durante la digitazione in Windows 10
Se scopri che il cursore del mouse salta o si sposta da solo, automaticamente, in modo casuale durante la digitazione nel laptop o nel computer Window...
Come invertire la direzione di scorrimento del mouse e dei touchpad in Windows 10
Topo e Touchpads non solo rende l'elaborazione semplice, ma anche più efficiente e richiede meno tempo. Non possiamo immaginare una vita senza questi ...