Передача параметров в лямбда в С++
Кажется, я пропустил какой-то момент в лямбда-механизме в С++. Вот код:
std::vector<int> vec (5);
int init = 0;
std::generate(begin(vec), end(vec), [init]() mutable { return ++init; });
for (auto item : vec) {
std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;
Если нет mutable
, он не будет компилироваться, потому что я изменяю init
в lambda.
Теперь, поскольку я понимаю, что lambda вызывается для каждого элемента вектора с новой новой копией init
, которая равна 0.
Итак, 1 нужно возвращать каждый раз.
Но выход этого фрагмента кода:
1 2 3 4 5
0
Похоже, что generate
захватывает копию init
только один раз в начале ее выполнения. Но почему? Предполагается, что он работает так?
Ответы
Ответ 1
Теперь, поскольку я понимаю, что lambda вызывается для каждого элемента вектора с новой новой копией init, которая равна 0.
Это неверно. Лямбда - это еще один способ сделать класс и предоставить operator()
для него. Часть []
лямбда описывает переменные-члены и записывается ли они по ссылке или значению. Элементом ()
лямбда является список параметров для operator()
, а часть {}
является телом этой функции. Часть mutable
сообщает компилятору сделать operator()
не const
, как это по умолчанию const
.
Итак,
[init]() mutable { return ++init; }
становится
struct compiler_generated_name
{
int init; // we captured by value
auto operator()() // since we used mutable this is non const
{
return ++init;
}
};
Я использовал структуру для краткости ввода, но лямбда указана как тип класса, поэтому class
может быть использован.
Это означает, что init
является тем же самым init
из последней итерации, поскольку вы только когда-либо захватываете один раз. Это важно помнить как
auto generate_lambda()
{
int foo = 0;
return [&foo](){ return ++foo; };
}
Покинет вас с dangling ссылкой на foo
, когда функция вернется и будет использовать поведение undefined.
Ответ 2
Лямбда - это сгенерированная компилятором структура, эквивалентная:
struct lambda
{
int init = 0; // captured value
auto operator()() // non-const, due to `mutable`
{
return ++init;
}
};
Следовательно, init
фиксируется и копируется внутри лямбда только один раз - вызов лямбда несколько раз не будет захватывать init
снова.
Ответ 3
Вы справляетесь и видите начальное значение init - то, что вы, вероятно, хотите сделать в соответствии с тем, что вы ожидаете, - это захват init
по ссылке.....
std::vector<int> vec (5);
int init = 0;
std::generate(begin(vec), end(vec), [&init]() mutable { return ++init; });
for (auto item : vec) {
std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;
Ответ 4
Ваша ошибка здесь. "Теперь, поскольку я понимаю, что лямбда вызывается для каждого элемента вектора с новой свежей копией init, которая равна 0" (курсив мой). Нет; поскольку вы можете видеть, что лямбда полностью разделена и, следовательно, невежественна, векторного кода. Инициализация item
происходит каждый раз, когда вычисляется сама лямбда-форма (в отличие от каждого момента, когда вызывается результирующее значение); здесь это означает каждый раз, когда вызывается функция generate
: только один раз.