Mi sono inmbattuto oggi in un problema abbastanza subdolo:
#include <iostream> #include <functional> #include <vector> using namespace std; function<void()> f() { vector<int> v(1000, 0); cout << "v size is: " << v.size() << endl; return [ v {move(v)} ]() { cout << "inside lambda => v size is: " << v.size() << endl; }; } int main() { f() (); }
Se eseguite il codice, l’output del programma è (ammetto con mia sorpresa) il seguente:
v size is: 1000
inside lambda => v size is: 1
La dimensione di v all’interno della lambda è 1. Ma perchè?
Il problema, come vedremo, non ha nulla a che vedere con le lambda in se ma risiede nel modo in cui v viene dedotto nella capture list.
Dato un vettore cosi definito:
vector<int> v(1000, 0); // crea 1000 interi inizializzati a 0
e altri due oggetti (t, u) cosi definiti:
vector<int> u{10,20,30}; auto t{10,20,30};
u è un vettore come tutti ce lo aspettiamo, completamente definito ed inizializzato (tramite la uniform initialization) con tre numeri (10, 20, 30), t invece è definito tramite auto. Il compilatore deduce t come std::initializer_list<decltype(v)> (= std::initializer_list<vector<int>>)!.
Ecco perchè v catturata dentro la lambda, definita implicitamente come auto, ha dimensione 1 (in quel caso abbiamo una std::initializer_list<vector<int>>). Per evitare il problema dobbiamo dunque riscrivere la nostra funzione in questo modo:
#include <iostream> #include <functional> #include <vector> using namespace std; function<void()> f() { vector<int> v(1000, 0); cout << "v size is: " << v.size() << endl; return [ v = move(v) ]() { //adesso v è un vector<int> cout << "inside lambda => v size is: " << v.size() << endl; }; } int main() { f()(); }
Finalmente il compilatore dedurrà correttamente v nella capture list come un vector<int>. Con questa modifica il risultato è quello atteso!
Concludendo: evitate di usare la brace initialization all’interno delle capture list, ma piuttosto utilizzate la notazione VAR = EXPR, perchè auto + brace initialization implica che il tipo dedotto sia una std::initializer_list.
Biblio: [Scott Meyers] – Item 7 More Effective C++