Может ли аргумент конструктора 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.
}