Il lambda espressione [&foo](int i){f(i, foo);}
porterà compilatore di generare una classe di chiusura qualcosa di simile (ma non del tutto corretto):
class _lambda
{
Foo& mFoo; // foo is captured by reference
public:
_lambda(Foo& foo) : mFoo(foo) {}
void operator()(int i) const
{
f(i, mFoo);
}
};
Pertanto, la dichiarazione Foo foo([&foo](int i){f(i, foo);});
viene considerata come Foo foo(_lambda(foo));
. Catturare lo stesso foo
durante la costruzione non presenta problemi in questa situazione, poiché qui è richiesto solo il suo indirizzo (i riferimenti vengono generalmente implementati tramite puntatori).
Il tipo std::function<void(int)>
sarà internamente copiare costruire questo tipo lambda, il che significa che l'argomento il costruttore di Foo fn
contiene una copia di _lambda
dell'oggetto (che contiene un riferimento (cioè, mFoo) al foo
).
Questi implica che problema di riferimento penzoloni può verificarsi in alcune situazioni, ad esempio:
std::vector<std::function<void(int)>> vfn; // assume vfn live longer than foo
class Foo {
Foo(std::function<void(int)> fn) { vfn.push_back(fn); }
}
void f(int i, Foo& foo) { /* stuff with i and foo */ }
Foo foo([&foo](int i){f(i, foo);});
....
void ff()
{
// assume foo is destroyed already,
vfn.pop_back()(0); // then this passes a dangling reference to f.
}
Probabilmente la pena notare che se si utilizza il lambda nel costruttore, cose cattive potrebbe accadere. –
se lo si cattura per copia si ottiene un avviso con clang 'la variabile non è inizializzata se utilizzata all'interno della propria inizializzazione' – Drax