Ответ
Основные риски связаны с управлением временем жизни объектов и семантикой захвата.
1. Висячие ссылки при захвате по ссылке ([&]):
auto make_dangerous_lambda() {
int local_var = 42;
// Захват по ссылке. Лямбда переживет 'local_var' -> неопределенное поведение.
return [&]() { std::cout << local_var; };
}
// Вызов возвращенной лямбды приведет к чтению мусора.
2. Неявный захват нежелательных переменных:
int global_counter = 0;
int value = 10;
// [=] захватывает ВСЕ нестатические локальные переменные по значению,
// но также неявно захватывает `global_counter` по ссылке!
auto lambda = [=]() { return value + global_counter; };
// Изменение global_counter извне влияет на результат лямбды.
3. Проблемы с производительностью и размером:
- Захват больших объектов по значению (
[=]) ведет к копированию. - Захват по ссылке (
[&]) может помешать оптимизациям компилятора. - Каждая лямбда имеет уникальный анонимный тип, что может увеличить размер шаблонного кода.
4. Сложности с рекурсией и явным указанием типа:
// Для рекурсии нужно использовать std::function или фиксировать тип через auto в аргументе.
std::function<int(int)> factorial = [&factorial](int n) -> int {
return n <= 1 ? 1 : n * factorial(n - 1);
};
// Или с использованием Y-комбинатора (более эффективно, но сложнее).
Лучшие практики:
- Явный захват: Используйте
[var1, &var2]вместо[=]или[&]. - Захват
this: В методах класса предпочитайте[this]или[=, this](C++20) вместо[&], чтобы избежать случайного захвата членов класса по ссылке. - Универсальные ссылки в шаблонах: Для generic-лямбд используйте
auto&&параметры. - Для асинхронных задач: Если лямбда выполняется позже, всегда захватывайте по значению (
[=]) или используйтеstd::shared_ptrдля разделяемых данных.