为什么C++中的lambda表达式使用函数对象而不是函数指针实现?

10
我发现MSVC和GCC中的lambda表达式都是实现了operator()的函数对象。它们为什么更喜欢使用函数对象而不是函数指针呢?

4
因为标准是这样规定的?他们也需要空间来进行捕捉。 - chris
3个回答

24
问题在于C ++中的lambda函数可以具有额外状态(即捕获变量或上下文环境),这些状态必须针对每个实例进行传递(它们可以针对相同lambda函数的每个实例不同)。
一个函数不能与您传递的句柄相关联的状态耦合。如果要向函数指针添加此类状态,则需要编写一个包装器,该包装器需要使用括号语法(operator())可调用,这恰好是functor的含义。
值得注意的是,一个没有捕获的lambda可以转换为函数指针。这仅可能是因为它不需要额外的空间。

我曾经阅读过一个提案,旨在将捕获lambda转换添加到函数指针中,其有效期与闭包实例相同。实际上,在大多数体系结构中,这是可能的,方法是编写包含机器语言指令和指向lambda的指针的数据,然后将其标记为可执行,并在运行时返回刚刚创建的该函数的指针。我不知道该提案的状态如何。 - Yakk - Adam Nevraumont
@Yakk 或者直接使用全局变量? ;) (事实上,这就是我在编辑答案时添加第一段最后一部分的原因。) - leemes
多个具有不同捕获状态的lambda实例将无法正常工作。基本上,在许多平台上,我们可以生成带有存储在它们所指向的代码中的状态的函数指针,并且存在大量不提供void*,void(*func)(void *)有状态回调对的遗留API。 - Yakk - Adam Nevraumont
@Yakk 哦,我没有完全理解你的意思。现在我明白了。但是对我来说听起来像是一个hack...此外,有一些(重要的)架构不允许这样做,据我所知(iOS?),但我可能错了。 - leemes

9

函数没有状态,因此它们无法实现lambda所需的功能。

此外,您可以确保lambda具有唯一的类型,这是仅仅使用函数无法实现的。


4

除了已经提到的捕获能力之外,性能也是一个原因。通常情况下,函数指针无法内联。而函数对象可以内联。这就是为什么 std::sort 比 qsort 更快的原因。如前所述,没有捕获的 lambda 可以转换为函数指针,但这主要是为了与旧的 c api 交互。例如,这样您就可以将 lambda 传递给旧的 win32 api 函数。一般来说,对于简单的 lambda,编译器更愿意将其内联。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接