我遇到了一个挑战我对C ++ lambdas的新生理解的情况,我把它简化为以下内容:
#include <iostream>
void test()
{
int (*func)();
func =
[]()->int {
std::cerr << "func()" << std::endl;
return 0;
};
int i = 0;
func =
[i]()->int {
std::cerr << "func(): i= " << i << std::endl;
return 0;
};
}
在第一种情况下,我将一个非常简单的lambda分配给一个函数指针,它看起来像我期望的那样工作。我在第二种情况下尝试做的是为lambda提供i
值的访问权限。我对[i]()->int {
代码 }
的理解是,它定义了一个不带参数的无名函数,返回int
,并通过C ++的魔力11个独角兽,知道i
的当前值。我希望这个lambda应该可以调用为int(*)()
。
test.cpp: In function ‘void test()’:
test.cpp:14:7: error: cannot convert ‘test()::__lambda1’ to ‘int (*)()’ in assignment
func =
^
gcc 4.8.1和4.8.2似乎不同意我的评估(4.4.1甚至拒绝讨论此事)。
这似乎表明第二个lambda的类型与函数指针不是赋值兼容的。我不明白为什么会出现这种情况,因为该表达式应该可以调用为“int(*)()
”。
我的理解(或独角兽)在哪里失败了?
答案 0 :(得分:9)
C ++标准,第5.1.2 / 6节,定义了lambda如何转换为(可能是模板)函数指针。
特别是:
非泛型lambda表达式的闭包类型,没有 lambda-capture具有公共非虚拟非显式const转换 用C指向函数的函数 ++语言链接(7.5)具有与闭包类型的函数调用操作符相同的参数和返回类型。返回的值 这个转换函数应该是一个函数的地址,当时 invoked,与调用闭包类型的函数具有相同的效果 呼叫运营商
由于lambda具有捕获功能,因此无法转换为函数指针。
注意:强>
即。以下内容无效:
int i = 0;
func =
[&]()->int {
std::cout << "func(): i= " << i << std::endl;
return 0;
}
<强> Live demo 强>
答案 1 :(得分:2)
使用签名int()
进行调用并不意味着可以将其转换为int(*)()
。任何带有重载int operator()()
的东西都可以这样调用。
Lambdas使用这样的重载创建漂亮的沼泽标准类,然后用一些语法糖包装它们。 (不是真的,但足够真实) 1
无国籍的lambdas(没有捕获任何东西)带有奖励operator int(*)()
超载(或一般operator Signature*
),但这只是一个奖励,而不是lambda的核心。
实际上,函数指针是调用函数时执行跳转到的地址。数据指针也没有空间(存储捕获变量的状态)。理论上,您可以在执行代码旁边或内部为数据分配空间,但是出于安全原因,许多系统会将可写页面标记为可执行,这使得该系统不切实际。有人提出过那种延伸。
1 与C ++标准中的许多内容一样,事物是根据行为而非实现来指定的。通过实现沼泽标准类&#34;自动编写&#34;可以复制lambdas的大多数功能。对你而言,即便如此,编译器也不必这样做。一些lambda实现(如基于[&]
lambdas的堆栈帧捕获)无法在C ++语言中实现。
答案 2 :(得分:2)
如果lambda携带状态,则无法将lambda转换为函数指针。如果你三思而后行,你会发现一个普通的函数指针不能从它的环境中携带状态。
所以你的第二个lambda不可转换,但它是第一个,因为第一个lambda相当于一个函数指针。