Страницы

Поиск по вопросам

суббота, 7 декабря 2019 г.

Список захвата в лямбда-функциях: есть ли смысл захватывать все переменные?

#cpp #cpp11 #lambda


Внутри квадратных скобок в определении лямбда-функции можно задать переменные из
внешнего контекста, которые нужны для работы этой лямбда функции. Можно их перечислить
вручную, а можно просто задать захват всех переменных сразу:

auto lambda1 = [x](int a) {return a < x; }; // Список захвата задан вручную
auto lambda2 = [&](int a) {return a < x; }; // Тупо захватываем всё по ссылке


Собственно вопрос: при каких условиях какой вариант предпочтителен? Есть ли вообще
смысл заморачиваться и указывать список захвата вручную, может быть проще всегда писать
[&] и не напрягать мозг, компилятор всё и так оптимизирует?
    


Ответы

Ответ 1



При захвате переменной по ссылке существует опасность что ссылка в итоге проживет дольше чем переменная, поэтому такого варианта надо избегать при работе с долгоживущими лямбдами. Кроме того, при использовании варианта "захват всего по значению", он же [=] есть риск получить циклическую ссылку или еще какую-нибудь ерунду по невнимательности. Поэтому для долгоживущих лямбд надо всегда явно указывать список захватываемых переменных: auto lambda1 = [x](int a) { return a < x; }; С другой стороны, если время жизни лямбды не превышает одного оператора в вашем коде - то захват всего по ссылке не сделает ничего страшного. Так, например, известно что std::for_each не сохраняет переданный ему функтор никуда: std::for_each(c.begin(), c.end(), [&](int a) { if (a < x) { std::cout << a << std::endl; } });

Ответ 2



Захватывать, следуя Мейерсу, по умолчанию вообще не стоит: по ссылке - чревато висячими ссылками, по значению - висячими указателями (особенно в плане this). Т.е. всегда используйте захват только того, что вам нужно, и так, как того требует код (что именно вы хотите достичь своей лямбдой).

Ответ 3



Единственная причина для захвата внешнего контекста целиком (да и то только по ссылке) - это инициализация при помощи лямбд (не помню, как этот подход называется), выглядит так: QString labelPref = "label_"; widget->setLayout([&]{ QHBoxLayout* layout = new QHBoxLayout; layout.addWidget(new QLabel(labelPref + "1")); layout.addWidget(new QLabel(labelPref + "2")); return layout; }() ); Тут висячую ссылку проблематично получить, но есть сомнения в целесообразности использования этого подхода.

Комментариев нет:

Отправить комментарий