Подтвердить что ты не робот

Где хранятся лямбда-захваченные переменные?

Как это работает? Он печатает 6:

#include <iostream>
#include <functional>

using namespace std;

void scopeIt(std::function<int()> &fun) {
    int val = 6;
    fun = [=](){return val;}; //<-- this
}

int main() {
    std::function<int()> fun;

    scopeIt(fun);

    cout << fun();

    return 0;
}

Где хранится значение 6, сохраненное после того, как scopeIt вызывается? Если я заменяю [=] на [&], он печатает 0 вместо 6.

4b9b3361

Ответ 1

Он хранится в закрытии, который - в вашем коде - сохраняется в std::function<int()> &fun.

Лямбда генерирует то, что эквивалентно экземпляру сгенерированного класса компилятора.

Этот код:

[=](){return val;}

Создает то, что фактически эквивалентно этому... это будет "закрытие":

struct UNNAMED_TYPE
{
  UNNAMED_TYPE(int val) : val(val) {}
  const int val;
  // Above, your [=] "equals/copy" syntax means "find what variables
  //           are needed by the lambda and copy them into this object"

  int operator() () const { return val; }
  // Above, here is the code you provided

} (val);
// ^^^ note that this DECLARED type is being INSTANTIATED (constructed) too!!

Ответ 2

Lambdas в С++ - это действительно "анонимные" функции структуры. Поэтому, когда вы пишете это:

int val = 6;
fun = [=](){return val;};

Что компилятор переводит в это:

int val = 6;
struct __anonymous_struct_line_8 {
    int val;
    __anonymous_struct_line_8(int v) : val(v) {}

    int operator() () const {
        return val; // returns this->val
    }
};

fun = __anonymous_struct_line_8(val);

Затем std::function сохраняет этот функтор с помощью типа стирания.

Когда вы используете [&] вместо [=], он меняет структуру на:

struct __anonymous_struct_line_8 {
    int& val; // Notice this is a reference now!
    ...

Итак, теперь объект хранит ссылку на объект функции val, который становится зависающей (недопустимой) ссылкой после выхода функции (и вы получаете поведение undefined).

Ответ 3

Так называемый тип замыкания (который является типом класса лямбда-выражения) имеет членов для каждого захваченного объекта. Эти элементы являются объектами для захвата по значению и ссылками для захвата по ссылке. Они инициализируются захваченными объектами и независимо друг от друга живут в закрывающем объекте (конкретном объекте типа закрытия, который обозначается этой лямбдой).

Неименованный элемент, который соответствует захвату значения val, инициализируется с помощью val и доступен изнутри типов закрытия operator(), что отлично. Объект закрытия может быть легко скопирован или перемещен несколько раз до тех пор, пока это не произойдет, и что слишком мелкие типы закрытия неявно определили конструкторы перемещения и копирования, как это делают обычные классы. Однако при захвате по ссылке преобразование lvalue-to-rvalue, которое неявно выполняется при вызове fun в main, вызывает поведение undefined как объекта, ссылка на упомянутый ссылочный элемент уже была уничтожена - т.е. мы используем ссылку на свинец.

Ответ 4

Значение лямбда-выражения является объектом типа класса, а

Для каждого объекта захваченный копией, в типе замыкания объявляется неназванный нестатический элемент данных.

([expr.prim.lambda]/14 в С++ 11)

То есть объект, созданный лямбдой

[=](){return val;}

фактически содержит нестатический член типа int, значение которого равно 6, и этот объект копируется в объект std::function.