lambda中的递归调用(C++ 11)

Naw*_*waz 18 c++ lambda type-inference auto c++11

可能重复:
c ++ 0x中的递归lambda函数

如果我把它写成:为什么我不能递归地调用lambda:

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

它给出了编译错误(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
Run Code Online (Sandbox Code Playgroud)

错误是什么意思?

我理解为什么我不能写这个:

auto i=i+1; //error: unable to deduce 'auto' from '<expression error>'
Run Code Online (Sandbox Code Playgroud)

我们不能写这个,因为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();
};
Run Code Online (Sandbox Code Playgroud)

编译罚款(ideone).但我仍然有兴趣知道该auto版本无法编译的原因.

Joh*_*itb 15

原因是auto变量的lambda-expression初始化器没有特殊情况.

这种特殊情况容易出错和误用.当你提出类似a()应该工作的东西时,你需要定义规则.怎么operator()抬头?什么是类型的准确状态a?这种类型会完整吗?(这意味着你已经知道了lambda的捕获列表).一旦您以符合规范的格式制定了该规范,就可以更容易地对其进行陈述.

让你的使用情况就意味着,你需要在代码扫描进取,因为确定的类型,另一起案件aa(),你必须确保初始化与没有任何可以"unlambda中"式结束

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

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

就像现在一样,a简单地禁止在其整个初始化程序中提及.因为在C++中,auto一种类型.它只是一个代表待推导类型的类型说明符.因此,表达式永远不会有auto类型.

  • "如果您通过引用捕获,那么一旦复制lambda并且原始a超出范围,您将调用一个悬空引用." - 当然,所有参考捕获都容易出现这种错误/误用,所以我怀疑它是否激励委员会不提供特殊情况,允许通过引用捕获"auto a". (3认同)

Fle*_*exo 6

在我看来,auto a案例和std::function<void()> a案例之间的重要区别在于,类型std::function<void()>不知道/关心它所指的真实函数的类型是什么.写作:

std::function<void()> a;
Run Code Online (Sandbox Code Playgroud)

非常好,在这里:

auto a;
Run Code Online (Sandbox Code Playgroud)

没什么意义.所以当合成捕获时,如果你使用std::function<void()>所有需要知道的类型已经知道,而auto它还不知道.

  • 我相信这是正确的答案,如果你能够知道编译器生成的lambda类型的名称(你不能),那么你可以在decl中使用该名称而不是`auto`.但是,我可以很容易地看到它处于"未定义的行为"类别中. (2认同)