Временные захваченные переменные в лямбда-функциях — C 11

#c #c 11 #lambda

#c #c 11 #лямбда

Вопрос:

Я пробовал что-то подобное, чтобы предварительно заполнить карту, используя векторный список строк. Код не требует пояснений:

 Constructor(const vector<string>amp; names) {
  for_each(names.begin(), names.end(),
                     [this, counter = 1](const Stringamp; choice) mutable {
                            nameMapping.emplace(choice, counter  );
                        }
  );
}
  

Что-то, что я действительно не понял, так это то, как это counter работает?

К вашему сведению: counter нигде не объявлено вне лямбда-функции.

Но все же я могу создать локальную переменную в области видимости класса и изменить ее в изменяемом лямбда-fn?

Может кто-нибудь, пожалуйста, помочь мне понять, что происходит.

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

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

2. Это как если бы вы могли писать auto counter = 1 .

Ответ №1:

Когда вы устанавливаете counter = 1 , вы объявляете новое временное counter значение, равное 1. Компилятор выполняет работу по определению типа. Этот временный объект int по умолчанию выводится в type и существует, пока лямбда активен.

Установив mutable , вы можете как изменять counter , так и this

Кроме того: поскольку кажется, что вы вставляете в карту / неупорядоченную карту, вам, вероятно, лучше использовать следующее:

 #include <algorithm> // For transform
#include <iterator>  // For inserter

Constructor(const vector<string>amp; names) {
    auto const example = [counter = 1](const stringamp; item) mutable {
        return {item, counter  };
    };
    std::transform(names.begin(), names.end(),
                   std::inserter(nameMapping, nameMapping.end()), example);
}
  

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

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

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

1. «Этот временный объект выводится с типом int по умолчанию» Потенциально запутанное использование термина «по умолчанию» здесь; это только int потому, что литерал 1 является an int .

2. В любом случае вам не нужно никаких сложностей. Лямбда, захват, mutable , std::transform , std::inserter , два заголовка… почему ?! Все, что вам нужно, это объявить, а int затем написать двухстрочный for цикл. Это действительно очень просто.

Ответ №2:

Но все же я могу создать локальную переменную в области видимости класса и изменить ее в изменяемом лямбда-fn?

Может кто-нибудь, пожалуйста, помочь мне понять, что происходит.

Все именно так, как вы сказали.

Возможно, это сбивает с толку, потому что в этом конкретном объявлении не указан тип. Лично я думаю, что это было ужасное дизайнерское решение, но мы идем.

Представьте auto counter = 1 , что вместо этого говорится; auto это сделано для вас. Затем переменная становится «членом» лямбда-объекта, присваивая ему состояние.

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

 Constructor(const vector<string>amp; names)
{
   int counter = 1;
   for (const stringamp; name : names)
      nameMapping.emplace(name, counter  );
}
  

На самом деле нет причин усложнять ситуацию только ради использования «причудливых» стандартных алгоритмов.