C++11 lambda returning lambda

匿名 (未验证) 提交于 2019-12-03 00:59:01

问题:

this piece of code is not something unknown to JS developers

function get_counter() {     return (         function() {             var c = 0;             return function() { return ++c; };         })(); }

it basically creates a which creates different enumerators. So I was wondering if same thing can be done in C++11 with new lambda semantics? I ended up writing this piece of C++ which unfortunately does not compile!

int main() {     int c;     auto a = [](){         int c = 0;         return [&](){             cout << c++;         };     };     return 0; }

so I was wondering if there is a workaround to get it compiled and if there is how can compiler make this code run correctly? I mean it has to create separate enumerators but it should also collect garbage (unused c variables).

by the way I'm using VS2012 compiler and it generates this error:

Error   2   error C2440: 'return' : cannot convert from 'main::::()::' to 'void (__cdecl *)(void)'    c:\users\ali\documents\visual studio 2012\projects\test\test\main.cpp   25  1   Test

回答1:

Your code has a bug in that it contains a dangling reference; the c reference will refer to the local variable in the outer lambda, which will be destroyed when the outer lambda returns.

You should write it using a mutable by-value lambda capture:

auto a = []() {     int c = 0;     return [=]() mutable {         cout << c++;     }; };

This relies on a post-standard extension to allow multiple statements in a return-type-deducing lambda; Is there a reason on not allowing lambdas to deduce the return type if it contains more than one statement? The easiest way to fix it is to supply a parameter so that the lambda contains only a single statement:

auto a = [](int c) {     return [=]() mutable {         cout << c++;     }; };

Unfortunately default parameters aren't allowed in lambdas, so you'd have to call this as a(0). Alternatively at the cost of readability you could use a nested lambda call:

auto a = []() {     return ([](int c) {         return [=]() mutable {             cout << c++;         };     })(0); };

The way this works is that when a executes the inner lambda copies all the referenced variables into an instance of its closure type, which here would be something like:

struct inner_lambda {     int c;     void operator()() { cout << c++; } };

The instance of the closure type is then returned by the outer lambda, and can be invoked and will modify its copy of c when called.

Overall, your (fixed) code is translated to:

struct outer_lambda {     // no closure     struct inner_lambda {         int c;    // by-value capture         // non-const because "mutable"         void operator()() { cout << c++; }     }     // const because non-"mutable"     inner_lambda operator()(int c) const {         return inner_lambda{c};     } };

If you left c as a by-reference capture, this would be:

struct outer_lambda {     // no closure     struct inner_lambda {         int &c;    // by-reference capture         void operator()() const { cout << c++; } // const, but can modify c     }     inner_lambda operator()(int c) const {         return inner_lambda{c};     } };

Here inner_lambda::c is a dangling reference to the local parameter variable c.



回答2:

It's a natural limitation of C++ that a lambda which captures by reference can't use the captured variable any more, once the variable no longer exists. So even if you get it to compile, you can't return this lambda from the function in which it appears (that also happens to be a lambda, but that's irrelevant), because the automatic variable c is destroyed on return.

I think the code you need is:

return [=]() mutable {     cout << c++; };

I haven't tested it and I don't know what compiler versions support it, but that's a capture-by-value, with mutable

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!