Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive call in lambda (C++11) [duplicate]

Possible Duplicate:
Recursive lambda functions in c++0x

Why can't I call a lambda recursively if I write it as:

auto a = [&]
{ 
   static int i = 0; i++;
   std::cout << i << std::endl; 
   if (i<10) 
     a(); //recursive call
};

It gives compilation error (ideone):

prog.cpp:8:18: error: '((const main()::<lambda()>*)this)->main()::<lambda()>::a' cannot be used as a function

prog.cpp: In function 'int main()':
prog.cpp:9:9: error: variable 'auto a' with 'auto' type used in its own initializer

What does the error mean?

I understand the reason why I can't write this:

auto i=i+1; //error: unable to deduce 'auto' from '<expression error>'

We can't write this because the type of i has to be deduced from it's initialization, which means the type cannot be deduced if i itself appears in the initialization (ideone). But how does it matter in case of lambda? If I'm not wrong, the type of a lambda is determined by it's parameter(s) and the return type; it doesn't depend on the body if it returns nothing (in which case, the return type is deduced as void, irrespective of other statements in the lambda-body).

Anyway, I got a workaround, and I can use std::function instead as:

std::function<void()> a = [&] 
{ 
   static int i = 0; i++;
   std::cout << i << std::endl; 
   if (i<10) 
      a();
};

which compile fines (ideone). But I'm still interested to know the reason why the auto version doesn't compile.

like image 475
Nawaz Avatar asked Oct 22 '11 17:10

Nawaz


3 Answers

The reason is that there is no special case for lambda-expression initializers of auto variables.

Such special cases would be prone to errors and misuses. You need to define the rules when you propose that something like a() should work. How is the operator() looked up? What is the precise state of a's type? Will the type be complete? (which implies that you already know the capture list of the lambda). Once you have formulated that in a format reasonable for a spec, it would be easier to make statements on it.

Allowing your use case would mean yet another case where you need to scan ahead in code, because to determine the type of a in a() you must be sure that the initializer ends with nothing that could "unlambda" the type

struct y { void operator()() { } };
template<typename T> y operator+(T, y) { return y(); }
auto x = [] { x(); } + y();

In this case, x() would call y::operator(), not the lambda.

As it is now, a is simply forbidden to be mentioned in its entire initializer. Because in C++, auto is not a type. It is merely a type specifier standing for a to-be-deduced type. As a consequence, an expression can never have type auto.

like image 144
Johannes Schaub - litb Avatar answered Oct 23 '22 15:10

Johannes Schaub - litb


As I see it the important difference between the auto a case and the std::function<void()> a case is that the type std::function<void()> doesn't know/care about what the type of the real function it refers to really is. Writing:

std::function<void()> a;

is perfectly fine, where as:

auto a;

makes little sense. So when the time comes to synthesize the capture if you use std::function<void()> all that needs to be known about the type is already known, whereas with auto it's not yet known.

like image 20
Flexo Avatar answered Oct 23 '22 13:10

Flexo


In a recursive function f is defined by f and return type of f is also determined by f in case of auto so it leads to infinite recursion.

when auto tries to derive a type. decltype(f()) will further deduce to another decltype(f)` as f derives to f e.g. a call on anything recursive is recursive too. return type determination turns recursive when applied on a recursive function. in a recursive function end of the recursion may be done on runtime. but determination is static only

like image 38
Neel Basu Avatar answered Oct 23 '22 14:10

Neel Basu