Recursive call in lambda (C++11)

Nawaz picture Nawaz · Oct 22, 2011 · Viewed 7.9k times · Source

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.

Answer

Johannes Schaub - litb picture Johannes Schaub - litb · Oct 22, 2011

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.