Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 lambda returning lambda

Tags:

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::<lambda_10d109c73135f5c106ecbfa8ff6f4b6b>::()::<lambda_019decbc8d6cd29488ffec96883efe2a>' to 'void (__cdecl *)(void)'    c:\users\ali\documents\visual studio 2012\projects\test\test\main.cpp   25  1   Test 
like image 528
Ali1S232 Avatar asked Sep 28 '12 11:09

Ali1S232


People also ask

Can you return a lambda function?

The lambda function can take many arguments but can return only one expression.

What is the return type of lambda?

The return type for a lambda is specified using a C++ feature named 'trailing return type'. This specification is optional. Without the trailing return type, the return type of the underlying function is effectively 'auto', and it is deduced from the type of the expressions in the body's return statements.

What is the correct syntax for lambda expression in C++11?

Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };

What does [&] mean in C++?

It's a lambda capture list and has been defined in C++ from the C++11 standard. [&] It means that you're wanting to access every variable by reference currently in scope within the lambda function.


2 Answers

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.

like image 74
ecatmur Avatar answered Nov 17 '22 12:11

ecatmur


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 to say that the captured value can be modified by the lambda.

So each time you call a you get a different counter with its own count starting from 0. Each time you call that counter, it increments its own copy of c. As far as I understand Javascript (not far), that's what you want.

like image 37
Steve Jessop Avatar answered Nov 17 '22 12:11

Steve Jessop