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

368
01 мая 2017, 03:28

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

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

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

Answer 1

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

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

Answer 2

При захвате переменной по ссылке существует опасность что ссылка в итоге проживет дольше чем переменная, поэтому такого варианта надо избегать при работе с долгоживущими лямбдами.

Кроме того, при использовании варианта "захват всего по значению", он же [=] есть риск получить циклическую ссылку или еще какую-нибудь ерунду по невнимательности.

Поэтому для долгоживущих лямбд надо всегда явно указывать список захватываемых переменных:

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;
  }
});
Answer 3

Единственная причина для захвата внешнего контекста целиком (да и то только по ссылке) - это инициализация при помощи лямбд (не помню, как этот подход называется), выглядит так:

QString labelPref = "label_";
widget->setLayout([&]{
    QHBoxLayout* layout = new QHBoxLayout;
    layout.addWidget(new QLabel(labelPref + "1"));
    layout.addWidget(new QLabel(labelPref + "2"));
    return layout;
    }()
);

Тут висячую ссылку проблематично получить, но есть сомнения в целесообразности использования этого подхода.

READ ALSO
SDL2 полностью статическая линковка. (MVS)

SDL2 полностью статическая линковка. (MVS)

1Я гружу с официального сайта код SDL 2

382
Как конвертировать символы unicodе в буквы русского алфавита

Как конвертировать символы unicodе в буквы русского алфавита

У меня есть строка "\u0410\u0410 1000 \u0410\u0410", это ответ с сервераМне нужно конвертировать ее, чтобы исходная срока имела вид: АА 1000 АА

387
Поиск в ширину или поиск в глубину

Поиск в ширину или поиск в глубину

Надо обойти все вершины графа в любом порядкеЧто лучше использовать: поиск в ширину или в глубину? Какие преимущества и недостатки у алгоритмов...

255
Переменная = empty

Переменная = empty

Можно ли в c++ новосозданной переменной присвоить значение empty?

314