Как устроены лямбды
Вопрос
Расскажите, как компилятор реализует лямбды, какие бывают захваты и чем они опасны.
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Closure type: безымянный класс, поля для захватов, const operator()
Захват по значению при создании, по ссылке без продления жизни
Init-capture и перемещение move-only объектов
mutable и состояние внутри объекта
Опасность [&] и [this] в отложенных вызовах
Уникальный тип, приведение к указателю без захватов, generic-лямбды
Что могут спросить следом
- Что захватывает [=] в методе класса?
- Как сделать рекурсивную лямбду?
Разбор
Для каждой лямбды компилятор создаёт безымянный класс: захваченные переменные становятся полями, тело становится константным operator(). Захват по значению копирует переменную в момент создания лямбды, по ссылке хранит ссылку и не продлевает жизнь. Инициализирующий захват [p = std::move(ptr)] позволяет перенести внутрь move-only объект или вычислить поле на месте. mutable снимает const с operator(), и лямбда может менять свои копии, то есть хранить состояние. Опасность в отложенных вызовах: лямбда с [&] или [this], отданная в поток, таймер или сохранённая как колбэк, переживает переменные или объект и обращается к висячей памяти. Поэтому в такие места я захватываю по значению, а для объекта беру weak_from_this или shared_from_this. У каждой лямбды уникальный тип, без захватов она приводится к указателю на функцию, а с auto в параметрах её operator() шаблонный.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.