Skip to content

Espressioni Lambda

Nella lezione precedente abbiamo faticato costruendo intere e verbose struct unicamente per sovraccaricare un operator(). Se devo passare un oggettino volante a std::sort solo una volta per ordinare degli elementi, non voglio inquinare il file scrivendo classi e costruttori!

Il C++ ha introdotto le Lambda Expressions (Funzioni Anonime). Le Lambda sono in assoluto la sintassi più rapida e meravigliosa per generare Funtori (Function Objects) “usa-e-getta” al volo.

Una lambda consta in 5 sezioni (molte delle quali opzionali):

[Catture] (Parametri) Modificatori -> TipoRitorno { Corpo }

Il tipo di ritorno è quasi sempre dedotto in automatico (auto), e spesso anche i Modificatori sono evitati. Rimane il nòcciolo vitale: [Catture](Parametri){ Corpo };

Il blocco parentesi quadre [] all’inizio si chiama Capture List. È ciò che permette alla tua funziona volante di “Catturare” (ingerire) le variabili locali del blocco in cui viene definita. È l’equivalente sintattico di passare parametri al costruttore del tuo Functor e salvarli in variabili di stato interne!

Se sei davvero pigro, puoi usare le Catture di Default:

  • [=] Cattura in automatico qualsiasi cosa scrivi nel corpo passandotelo per COPIA.
  • [&] Cattura in automatico qualsiasi variabile che tu scriva, per REFERENZA.

Avvertenza di sicurezza: Sebbene seducente, usare [&] è pericoloso se passi questa Lambda in giro (magari ad altri thread). Il blocco main potrebbe spegnersi, la variabile morirebbe e la Lambda punterebbe al vuoto causando Segmentation Fault. È preferibile scrivere in modo esplicito (Named Capture) [&mio_valore] per capire subito chi ha i permessi di lettura/scrittura.

Normalmente le variabili catturate per valore (Copia) divengono const internamente e non puoi modificarle per fare calcoli (sono in Read-Only all’interno del corpo { }). Per renderle alterabili (senza però riflettere le modifiche fuori all’esterno) basta usare il modificatore mutable: [copia_tally]() mutable { copia_tally++; };

Con le Generic Lambdas puoi mescolare la magia dei Template con le funzioni anonime in una formula spettacolare. Basta rimpiazzare i tipi dei parametri con la parola auto.

// Il compilatore estrae un template su due piedi!
auto translate = [](auto x) { return 10 * x + 5; };
// Potro' passargli INT (chiamera' l'instanziazione int)
int a = translate(10);
// O double (chiamera' l'instanziazione double)
double b = translate(5.5);

Tutte le Lambda create possono essere valutate in un regime constexpr in C++17 se rispettano i vincoli. Scrivere algoritmi pesanti che si autocalcolano nel silicio durante la compilazione non è mai stato così snello ed elegante.