lambda capture by reference (tsz. lambda capture by references)
A lambda szintaxisa:
(parameter_list) -> return_type { body }
A capture
listában megmondhatjuk, hogy a lambda milyen külső változókat “vigyen magával”, és hogyan:
by value
) → másolatby reference
) → hivatkozásMost a referencia szerinti befogást vizsgáljuk.
Capture by reference azt jelenti, hogy a lambda nem másolatot készít a változóról, hanem hivatkozást visz magával:
Szintaxis:
// csak az 'x' változót fogjuk be referenciaként
// minden használt külső változót referenciaként fogunk be
#include <iostream>
int main() {
int x = 10;
auto lambda = () {
std::cout << "x inside lambda: " << x << std::endl;
};
x = 20;
lambda();
}
Kimenet:
x inside lambda: 20
Miért?
miatt a lambda referenciaként viszi magával az x
-et.x = 20;
megtörténik, a lambda is látja az új értéket.
Ha az összes használt külső változót referenciaként akarod befogni, használhatod a szintaxist:
#include <iostream>
int main() {
int x = 5;
int y = 8;
auto lambda = () {
std::cout << "x: " << x << ", y: " << y << std::endl;
};
x = 100;
y = 200;
lambda();
}
Kimenet:
x: 100, y: 200
Capture by reference akkor hasznos, ha:
✅ A lambda futásakor mindig az aktuális értéket akarod látni ✅ A lambdán belül módosítani akarod a külső változót ✅ Nagy méretű objektumokat használsz, amiket nem akarsz másolni
Nézzünk egy példát, ahol a lambda módosítja a külső változót:
#include <iostream>
int main() {
int counter = 0;
auto increment = () {
++counter;
};
increment();
increment();
increment();
std::cout << "counter: " << counter << std::endl;
}
Kimenet:
counter: 3
Itt a lambda minden hívásnál megnöveli a counter
változót, mert referenciaként van befogva.
⚠️ Élettartam probléma: ha a lambda egy másik szálon vagy később fut le, és a referenciázott változó már nem él, abból hiba lesz (dangling reference).
Példa:
#include <iostream>
#include <functional>
std::function<void()> makeLambda() {
int x = 42;
return () {
std::cout << "x: " << x << std::endl; // HIBA: x megszűnik
};
}
int main() {
auto lambda = makeLambda();
lambda(); // undefined behavior!
}
Ezért ha a lambda hosszabb élettartamú (pl. másik szálban fut), érték szerint érdemes capture-ölni.
Capture by Value
|
Capture by Reference
|
---|---|
Másolat készül | Referencia kerül be |
Eredeti változás nem látszik | Eredeti változás látszik |
Thread-safe | Nem thread-safe (vigyázni kell) |
Nagy objektumok → drága lehet | Nagy objektumok → olcsó, nincs másolat |
Lehet vegyesen is használni:
auto lambda = () {
// x by value, y by reference
};
x
másolat → nem fog látni változásty
referencia → látni fogja a változást
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v{1, 2, 3, 4, 5};
int sum = 0;
std::for_each(v.begin(), v.end(), (int x) {
sum += x;
});
std::cout << "Sum: " << sum << std::endl;
}
Kimenet:
Sum: 15
Itt a sum
változót referenciaként fogjuk be, hogy a lambda minden egyes elemnél hozzáadhassa az értéket.
✅ Nem kell másolatot készíteni
✅ Módosítható a külső változó
✅ Hatékony nagy objektumoknál
✅ Az aktuális értéket látjuk futáskor
⚠️ Vigyázni kell az élettartamra → dangling reference veszély ⚠️ Nem thread-safe ha a külső változót más szál is módosítja ⚠️ Nehéz debuggolni, ha nem világos, mit viszünk be referenciaként
→ biztonságos
→ de figyelj az élettartamra!