Может ли аргумент конструктора lambda С++ захватить построенную переменную?

Следующие компиляции. Но существует ли какая-либо проблема обмана?

    class Foo {
         Foo(std::function<void(int)> fn) { /* etc */ }
    }

    void f(int i, Foo& foo) { /* stuff with i and foo */ }

    Foo foo([&foo](int i){f(i, foo);});

Кажется, нужно работать. (Настоящая лямбда, конечно, сложнее.)

Ответ 1

Но существует ли какая-либо проблема с обманутой ссылкой?

Это полностью зависит от того, что вы делаете с Foo. Вот пример, который мог бы оборвать ссылки:

struct Foo {
     Foo() = default;
     Foo(std::function<void(int)> fn) : fn(fn) { }
     std::function<void(int)> fn;
}

Foo outer;
{
    Foo inner([&inner](int i){f(i, inner);});
    outer = inner;
}
outer.fn(42); // still has reference to inner, which has now been destroyed

Ответ 2

Выражение лямбда [&foo](int i){f(i, foo);} приведет к компилятору для создания класса закрытия что-то вроде этого (но не полностью правильно):

class _lambda
{
    Foo& mFoo; // foo is captured by reference

public:
    _lambda(Foo& foo) : mFoo(foo) {}

    void operator()(int i) const
    {
       f(i, mFoo);
    }
};

Следовательно, объявление Foo foo([&foo](int i){f(i, foo);}); рассматривается как Foo foo(_lambda(foo));. Захват foo сам при построении не имеет проблемы в этой ситуации, потому что здесь требуется только его адрес (ссылки обычно реализуются с помощью указателей).

Тип std::function<void(int)> будет внутренне скопировать этот лямбда-тип, что означает, что аргумент конструктора Foo fn содержит копию объекта _lambda (который содержит ссылку (т.е. mFoo) на ваш foo).

Это означает, что в некоторых ситуациях может возникнуть ошибка, связанная с ссылкой, например:

std::vector<std::function<void(int)>> vfn; // assume vfn live longer than foo

class Foo {
     Foo(std::function<void(int)> fn) { vfn.push_back(fn); }
}

void f(int i, Foo& foo) { /* stuff with i and foo */ }

Foo foo([&foo](int i){f(i, foo);});

....

void ff()
{
    // assume foo is destroyed already,
    vfn.pop_back()(0); // then this passes a dangling reference to f.
}