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++
