Когда в определении лямбда-выражения C ++ 11 требуются захваты переменных с явным именем? - PullRequest
1 голос
/ 21 мая 2019

До сих пор, рассматривая лямбда-выражения C ++ в стиле c++11 , я поместил их все в две широкие категории: захват и отсутствие захвата.

Невозвратные лямбды, хотя и более ограничены в том, как их можно записать, гораздо более гибки в том, как их можно использовать - они могут неявно преобразовываться в аналогичные типы указателей на функции ;они не поощряют необоснованное std::function<…> использование, их область применения менее вероятна выползти и вызывать побочные эффекты , и так далее.

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

Это, насколько я понимаю, однако.Когда я использую захват лямбд, я склонен явно ссылаться на определенные переменные, когда есть между одной и двумя необходимыми мне переменными:

using lambda_t = std::function<std::add_pointer_t<void>(int)>;

lambda_t lambda_explicit = [&one, &another](int descriptor) {
    return ::mmap(nullptr, one, PROT_READ, MAP_PRIVATE, descriptor, another);
};

… если их больше двух, я предпочитаю (из равныхчасти синтаксического OCD и лени), чтобы избежать явно названных захватов в пользу формы ссылки на все:

lambda_t lambda_everything = [&](int descriptor) {
    return ::mmap(nullptr, one, PROT_READ, MAP_PRIVATE, descriptor, another);
};

… которые отмечают, что изменение формы захватов лямбды не меняет ничего очевидного в лямбда-выраженияхtype - подпись вызова такая же, например.Это нелогично, так как кажется, что большая часть способа захвата работ неопределенно задана и в некоторой степени зависит от реализации , своего рода обратно пропорционально подробному формальному разнообразию, предоставляемому выражением захвата (или это декларация? Илисписок деклараций? ... Я не уверен) полную славу, которую вы увидите, если вы перейдете к этой последней ссылке и прокрутите немного вниз.

Я даже не коснулся подавляющего большинстваиз возможностей - я почти всегда просто делаю:

  1. Нет захвата вообще;
  2. Одна или две явно названные переменные, захваченные по ссылке;или
  3. Неизбирательный захват по ссылке всего: [&]

Каковы обстоятельства, при которых я должен избегать использования одной формы захвата надДругой?

Какие формы особые, и их вообще следует избегать?Какие из них имеют ощутимые штрафы - в производительности, размере кода, потенциальной UB или что-то еще?Имеют ли какие-либо формы захвата ощутимые и / или легкие преимущества?

Ответы [ 2 ]

4 голосов
/ 21 мая 2019

В каких обстоятельствах я должен избегать использования одной формы захвата над другой?

Когда вы ограничены использованием функции без сохранения состояния, захват не является опцией, и вы ограничены 1. Это обычно имеет место при регистрации обратного вызова в API языка C.

Когда вы хотите переместить объект в захват, вы должны переместить его в именованный захват и, следовательно, ограничиться 2. Это невозможно в C ++ 11; вам нужны обобщенные лямбда-захваты из C ++ 14. Обобщенные захваты также полезны для изменчивых лямбд с начальным состоянием, которые не нужно сообщать извне (кроме случаев, когда явно возвращается).

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

Я не думаю, что когда-либо необходимо использовать захват по умолчанию, но это может сэкономить много лишней печати. ​​

1 голос
/ 21 мая 2019

Я предпочитаю использовать именованные захваты по той же причине, по которой мне не нравятся глобальные переменные. Это дает возможность лямбда-кода.

Очень быстро становится невозможно провести аудит кода, если имеется много переменных и много лямбда-выражений, обращающихся только к некоторым переменным с неназванным синтаксисом. Хорошо, если у вас есть небольшая функция с 3 локальными переменными, то легко принять, что лямбда может видеть все три переменные, но если у вас 10 локальных переменных, вы будете тратить время на пересмотр кода, пытаясь понять, какие три переменные Лямбда на самом деле модифицирует.

...