2011-10-22 69 views
18

可能重复:
Recursive lambda functions in c++0x递归调用(C++ 11)

为什么我不能叫拉姆达递归,如果我把它写成:

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

它给出编译错误(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 

错误是什么意思?

我明白其中的道理,为什么我不能这样写:

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

我们不能写,因为i的类型必须推断,从它的初始化,这意味着无法被推断,如果该类型i本身出现在初始化中(ideone)。但是在lambda的情况下它又有什么关系?如果我没有错,lambda的类型由它的参数和返回类型决定;如果它不返回任何内容,则不依赖于主体(在这种情况下,返回类型推导为void,而不考虑lambda主体中的其他语句)。

无论如何,我得到了一个解决办法,我可以使用std::function代替为:

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

它编译罚款(ideone)。但我仍然很想知道为什么auto版本不能编译的原因。

+0

我认为这个问题与你最终隐式捕获'a'有关,因为'a'本身最终会成为'a'的成员,因此会无限递归。 – Flexo

+0

@awoodland:这是什么意思?请详细说明。 – Nawaz

+3

lambda的类型也取决于捕获,我怀疑这是问题。如果你不使用'a',那么编译器不关心,但是当你这样做的时候它不知道要使用什么类型的对象。 –

回答

15

原因是没有特殊的情况为auto变量的lambda表达式初始值设定项。

这种特殊情况容易出错和误用。当您建议像a()这样的东西应该工作时,您需要定义规则。 operator()是怎么查找的? a的类型的确切状态是什么?该类型是否完整? (这意味着你已经知道lambda的捕获列表)。一旦你以一种合理的规格形式制定了这个规范,就可以更容易地做出陈述。

允许你的使用情况就意味着,你需要在代码扫描进取,因为确定a()a类型,你必须确保初始化什么也没有结束另一起案件中,可以“unlambda中”类型

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

在这种情况下,x()将调用y::operator(),而不是lambda。

就像现在一样,a在其整个初始化程序中被禁止提及。因为在C++中,auto而不是类型。它仅仅是一个说明符,表示一个将被推断的类型。因此,表达式不能有自动

+3

“如果你通过引用捕获了一个引用,那么当你拷贝你的lambda并且原始a超出了范围时,你将会调用一个引用引用。 - 当然,所有通过引用进行捕获都容易出现这种错误/误用,所以我怀疑它是否会激励委员会不提供特殊情况来允许通过引用捕获“auto a”。 –

+0

再加上捕获'a'的问题,无法递归调用闭包的另一个原因是否则无法引用闭包对象:'this'指定封闭类对象(如果有的话)。 –

6

正如我所看到的,auto a案例与std::function<void()> a案例之间的重要区别在于std::function<void()>类型不知道/关心它所指的实际函数的类型究竟是什么。写作:

std::function<void()> a; 

是完全正常的,在那里为:

auto a; 

变得毫无意义。因此,如果您使用std::function<void()>来综合捕获时间,那么所有需要了解的类型已知,而auto目前还不知道。

+2

我相信这是正确的答案,如果你能够知道编译器生成的lambda类型的名称(你不能),那么你可以在decl中使用该名称而不是'auto'。但是,我可以很容易地看到这是“未定义的行为”类别。 –

+0

@MichaelPrice - 我试图挖掘出“你永远不会知道lambda类型的名称”这个答案的引用,但我不记得它在哪个部分。 – Flexo

2

在一个递归函数ff定义和返回的f类型也由fauto情况下确定以便它会导致无限递归。

auto试图派生一个类型。 decltype(f())将进一步推导为另一个decltype(f)`,因为f从f导出。任何递归调用也是递归的。当应用于递归函数时,返回类型确定变为递归。在递归函数中递归的结束可以在运行时完成。但决心只是静止

+0

@Neel:这不对我有意义。当体内没有返回声明时,它怎么能返回任何东西?你是否暗示“深层递归层次”意味着lambda-body无限大,无法知道return语句的存在或不存在? – Nawaz

+0

@awoodland:谢谢,I'vent看了看评论。 @Nawaz:在每一个函数中都存在一个数学可能性,只为可视化返回'return'语法的语法扫描。 –

+3

+1适合图片 –