Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Понимание Capture в лямбда-функциях C++
"Поля" в лямбда-функции — это механизм захвата (capture) переменных из окружающей области видимости. Лямбда-функция может использовать переменные, определённые за её пределами, только если явно их захватить.
Что такое capture list []
Фигурные скобки [] в начале лямбды определяют, какие переменные из внешней области видимости будут доступны внутри лямбды:
int x = 5;
int y = 10;
// [x, y] — захватываем обе переменные
auto lambda = [x, y]() {
std::cout << x << " " << y << std::endl; // Ошибка! x и y не определены
return x + y;
};
auto lambda2 = [x, y]() {
return x + y; // OK! x и y захвачены
};
Типы захвата
[=] — захват по значению (copy)
int x = 5;
auto lambda = [=]() {
std::cout << x << std::endl; // x = 5, копия
};
x = 20; // Меняем оригинальный x
lambda(); // Выведет 5, не 20! (была копия)
Преимущества:
- Лямбда независима от изменений в оригинальных переменных
- Потокобезопасна
- Предсказуемо
Недостатки:
- Затраты памяти на копирование
- Если переменная большой объект (vector), копируется целая копия
[&] — захват по ссылке (reference)
int x = 5;
auto lambda = [&]() {
std::cout << x << std::endl;
};
x = 20;
lambda(); // Выведет 20! (ссылка на оригинал)
Преимущества:
- Никакого копирования, экономия памяти
- Видим актуальные изменения переменной
Недостатки:
- ОПАСНО! Если переменная выходит из области видимости, ссылка становится невалидной
- Race conditions при многопоточности
[x] — захват конкретной переменной по значению
int x = 5, y = 10;
auto lambda = [x]() {
return x * 2; // x захвачена
// return y * 2; // Ошибка! y не захвачена
};
[&x] — захват конкретной переменной по ссылке
int x = 5, y = 10;
auto lambda = [&x]() {
return x * 2; // x захвачена по ссылке
// return y * 2; // Ошибка! y не захвачена
};
[=, &x] — смешанный захват (большинство по значению, x по ссылке)
int x = 5, y = 10;
auto lambda = [=, &x]() {
x = 100; // OK! x по ссылке, можем менять
// y = 100; // Ошибка! y по значению, readonly
};
[&, x] — смешанный (большинство по ссылке, x по значению)
int x = 5, y = 10;
auto lambda = [&, x]() {
y = 100; // OK! y по ссылке
// x = 100; // Ошибка! x по значению, readonly
};
Практический пример: обработка данных
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> data = {1, 2, 3, 4, 5};
int multiplier = 2;
int sum = 0;
// Захватываем multiplier по значению, sum по ссылке
std::for_each(data.begin(), data.end(),
[multiplier, &sum](int value) {
int result = value * multiplier;
sum += result; // Можем менять sum (по ссылке)
std::cout << result << " ";
}
);
std::cout << "\nSum: " << sum << std::endl; // sum = 2+4+6+8+10 = 30
return 0;
}
Mutable лямбды
По умолчанию переменные, захваченные по значению, readonly:
int x = 5;
auto lambda = [x]() {
// x = 10; // Ошибка! x const
std::cout << x << std::endl;
};
// Решение: mutable
auto lambda_mutable = [x]() mutable {
x = 10; // OK! Меняем копию, не оригинал
std::cout << x << std::endl;
};
Когда и что захватывать
Захватывайте по значению [=], если:
- Лямбда будет вызвана позже (callback, async задачи)
- Нужна потокобезопасность
- Переменная может выйти из области видимости
Захватывайте по ссылке [&], если:
- Лямбда вызывается сразу в одной области видимости
- Переменная гарантированно жива во время вызова
- Нужно сэкономить память (большие объекты)
Опасность: Dangling references
std::function<void()> create_lambda() {
int x = 5;
return [&x]() { // ОПАСНО! &x выходит из области видимости
std::cout << x << std::endl;
};
}
int main() {
auto lambda = create_lambda();
lambda(); // Undefined behavior! x уже не существует
}
Итог
"Поля" в лямбде (capture list) позволяют лямбда-функции использовать переменные из внешней области. Правильный выбор между захватом по значению и по ссылке критически важен для безопасности и производительности кода.