C ++ 11 lambda returning lambda

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 counters. So I was wondering, is it possible to do the same in C ++ 11 with the new lambda semantics? I ended up writing this C ++ piece, 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 that can be compiled, and if there is a way so that the compiler can run this code correctly? I mean, it should create separate counters, but it should also collect garbage (unused c variables).

By the way, I use the 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 
+20
c ++ lambda c ++ 11 visual-c ++ visual-studio-2012
Sep 28 '12 at 11:58
source share
5 answers

There is an error in your code because it contains a broken link; reference c will refer to a local variable in the outer lambda that will be destroyed when the outer lambda returns.

You must write it using mutable lambda bypass capture:

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

It depends on a post-standard extension allowing multiple operators to be used in lambda with a return type; Is there a reason lambdas does not allow the return of a return type if it contains more than one statement? The easiest way to fix this is to specify a parameter so that the lambda contains only one statement:

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

Unfortunately, the default options are not allowed in lambdas, so you should call this as a(0) . Alternatively, due to readability, you can use the nested lambda call:

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

How it works, when a executes an internal lambda it copies all the reference variables into an instance of its closure type, which will be something like this:

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

Then the instance of the closure type is returned by the external lambda and can be called and will change its copy c when called.

In general, your (fixed) code translates 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 capture by reference, it 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 a local parameter variable c .

+16
Sep 28 '12 at 12:14
source share

This is a natural C ++ limitation that lambda, which captures by reference, can no longer use the captured variable as soon as the variable no longer exists. Therefore, even if you compile it, you cannot return this lambda from the function in which it appears (which is also a lambda, but this is inappropriate), because the automatic variable c destroyed upon return.

I think you need the code:

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

I have not tested it, and I do not know which versions support it, but this is a capture by value, and mutable means that the fixed value can be changed by lambda.

Therefore, every time you call a , you get another counter with your own count starting with 0. Each time you call this counter, it increments its own copy of c . As far as I understand Javascript (not far), this is what you want.

+9
Sep 28 '12 at 12:11
source share

I think the problem is that the compiler cannot output the return type of the external lambda (which is assigned to a ), because it consists of a simpler return of one line. But, unfortunately, there is also no way to explicitly specify the type of inner lambda. Therefore, you will need to return std::function , which will contain additional overhead:

 int main() { int c; auto a = []() -> std::function<void()> { int c = 0; return [=]() mutable { std::cout << c++; }; }; return 0; } 

And, of course, you have to catch by value, as Steve has already explained in his answer.

EDIT: As for the exact error, it cannot convert the returned inner lambda to void(*)() (pointer to the void() function), I have only some guesses, because I do not have much understanding of their lambda implementation, that I'm not sure if it is stable or standard compatible in general.

But I think that VC is at least trying to infer the return type of the internal lambda and realizes that it returns the callable. But then it somehow incorrectly assumes that this inner lambda does not capture (or they cannot determine the inner lambda type), so they simply force the outer lambda to return a simple function pointer that really works if the inner lambda does not capture everything.

EDIT: And, like the ecatmur expressions in his comment, returning std::function even necessary when creating the actual get_counter function (instead of lambda), since normal functions do not have any automatic output type inference.

+7
Sep 28 '12 at 12:19
source share

The first thing you should know is that even if you get the syntax to compile, the semantics are different. In C ++, lambdas that capture by reference are only a regular link, which will not extend the lifetime of the object associated with that link. That is, the lifetime c is related to the lifetime of the lambda:

 int main() { int c; auto a = [](){ int c = 0; return [&](){ return ++c; }; }(); // Note: added () as in the JS case std::cout << a() << a(); return 0; } 

After adding the missing () so that the outer lambda is evaluated, your problem is that the c that is held by the link in the returned lambda is no longer valid after evaluating the full expression.

It’s not too difficult to do this work due to the additional dynamic distribution (which would be equivalent to the JS case):

 int main() { int c; auto a = [](){ std::shared_ptr<int> c = std::make_shared<int>(0); return [=](){ return ++(*c); }; }(); // Note: added () as in the JS case std::cout << a() << a(); return 0; } 

This should compile and work properly. Whenever the inner lambda is freed ( a goes beyond), the counter will be freed from memory.

+3
Sep 28 '12 at 12:18
source share

This works on g ++ 4.7

 #include <iostream> #include <functional> std::function<int()> make_counter() { return []()->std::function<int()> { int c=0; return [=]() mutable ->int { return c++ ; }; }(); } int main(int argc, char * argv[]) { int i = 1; auto count1= make_counter(); auto count2= make_counter(); std::cout << "count1=" << count1() << std::endl; std::cout << "count1=" << count1() << std::endl; std::cout << "count2=" << count2() << std::endl; std::cout << "count1=" << count1() << std::endl; std::cout << "count2=" << count2() << std::endl; return 0; } 
Valgrind doesn't complain at all about this. Every time I call make_counter, valgrind reports an additional allocation and is free, so I assume that the metaprogram code lambda inserts a memory allocation code for variable c (I think I can check the debugger). I wonder if this matches Cxx11 or just g ++. Clang 3.0 will not compile this because it does not have a std :: function (maybe I can try using the boost function).
+3
Feb 23 '13 at 18:05
source share



All Articles