В обсуждении ранее я сказал, что настоящие лямбды/замыкания не могут использовать стековые переменные, так как стек разрушается. И пример привел, который бы в javascript работал, а в C++ нет.
Но частично эта проблем решена. Когда вы создаете лямбду в функции и возвращаете её наружу, то
То реально создается две лямбды, одна в функции main. А вторая в функции test2. И при выходе из test2, одна лямбда просто копируется в другую (указатель на код и входные параметры просто копируются во внешнюю переменную). Как бы это было с числом, строкой и любым другим объектом, который можно копировать.
Вот еще пример, в котором видно, что объекты находятся в стеке. И каждое присвоение переменной вызывает копирование объекта.
Единственное, чего я не понял, это почему все объекты внутри лямбда функции константные. (в камментах уже объяснили, я забыл про слово mutable).
(Не всегда в стеке. Если данные не помещаются в std::function, размер которого всего 24 байта, то объект создается в куче, но сути дела это не меняет.)
Обойти постоянное копирование можно завернув аргументы в std::make_shared.
А если std::make_shared завернуть не аргументы лямбда-функции, а все переменные функции, в которой эта лямбда создавалась, то получим полноценное замыкание в стиле JavaScript.
Но частично эта проблем решена. Когда вы создаете лямбду в функции и возвращаете её наружу, то
function<string()> test2(string x) { return [x] { return x; }; } void main() { auto fn = test2("Hello world"); }
То реально создается две лямбды, одна в функции main. А вторая в функции test2. И при выходе из test2, одна лямбда просто копируется в другую (указатель на код и входные параметры просто копируются во внешнюю переменную). Как бы это было с числом, строкой и любым другим объектом, который можно копировать.
Вот еще пример, в котором видно, что объекты находятся в стеке. И каждое присвоение переменной вызывает копирование объекта.
Единственное, чего я не понял, это почему все объекты внутри лямбда функции константные. (в камментах уже объяснили, я забыл про слово mutable).
class Check { public: int c; Check() { messageBox(hex(int(this))+" constructor", "", 0); c=0; } Check(const Check& src) { messageBox(hex(int(this))+" copy", "", 0); c=0; } int get() { return c++; } }; std::function<std::string()> test2(std::string x) { return [x] { return x; }; } void test() { Check c; // 1EBEC78 constructorauto fn1 = [c] { return const_cast<Check*>(&c)->get(); }; // 1EBEC74 copyauto fn1 = [c] mutable { return c.get(); }; // 1EBEC74 copy auto fn2 = fn1; // 1EBEC70 copy int res1 = fn1(); // 0 int res2 = fn1(); // 1 int res3 = fn2(); // 0 }
(Не всегда в стеке. Если данные не помещаются в std::function, размер которого всего 24 байта, то объект создается в куче, но сути дела это не меняет.)
Обойти постоянное копирование можно завернув аргументы в std::make_shared.
auto data = std::make_shared<int>(0); auto fn = [data]() { return (*data)++; };
А если std::make_shared завернуть не аргументы лямбда-функции, а все переменные функции, в которой эта лямбда создавалась, то получим полноценное замыкание в стиле JavaScript.