C ++中lambda和常规函数的不同堆栈深度?
Posted
技术标签:
【中文标题】C ++中lambda和常规函数的不同堆栈深度?【英文标题】:Different stack depth for lambdas and regular functions in C++? 【发布时间】:2017-02-03 16:57:54 【问题描述】:考虑一个普通的递归函数:
#include <iostream>
#include <functional>
void f(unsigned long long int x)
std::cout << x << "\n";
if(x < 1e9)
f(x+1);
int main()
f(1);
return 0;
这终止于 43033。
现在考虑一个递归 lambda:
#include <iostream>
#include <functional>
int main()
std::function<void(int)> g = [&g](unsigned long long int x)
std::cout << x << "\n";
if(x < 1e9)
g(x+1);
;
g(1);
return 0;
这会在 11736 的堆栈深度低得多时终止。
为什么 lambda 的最大堆栈深度较低?
(使用g++ (GCC) 5.4.0
编译,使用-std=c++14 -Wall
)
另请注意,使用-O3
优化进行编译允许几乎无限递归深度,但 lambda 仍终止于 25k。
编辑:在@Yakk 之后,这里是Y-combinator 的结果:
#include <iostream>
#include <functional>
using namespace std;
template <typename T, typename R>
function<R(T)> Y(function<function<R(T)>(function<R(T)>)> f)
// Y f = f (λx.(Y f) x)
return f([=](T x) return Y(f)(x); );
int main()
using fg = function<void(int)>;
function<fg(fg)> sg = [](fg g)
return [g](unsigned long long int x)
std::cout << x << "\n";
if(x < 1e9)
g(x+1);
;
;
Y(sg)(1);
return 0;
这在 4781 和 9221 分别有和没有-O3
终止。
【问题讨论】:
@DieterLücking 详细说明? 尝试直接使用 lambda (auto f
) 我认为开销在std::function
@Motti 它不会编译,因为它需要事先知道函数签名才能递归!
@prakharsingh95,好点,反正@Yakk 说开销在std::function
中,而不是在 lambda 中。
栈可以容纳 N 个字节。您可以在堆栈中放入 M 个小部件或 K 个小工具。小部件和小工具占用不同的字节数。又是什么问题?
【参考方案1】:
std 函数与 lambda 的含义不同。 std 函数是一个能够存储一些 lambda 表达式的对象,或者一个函数指针,或者一个指向成员函数的指针,或者一个指向成员数据的指针,或者几乎任何兼容地覆盖 operator() 的对象。
在 std 函数中存储 lambda 时,会产生一些开销。不多,但有一些。其中一些开销可能表现为更多地使用堆栈(并且在未优化的构建中开销会更大)。
您可以通过使用 y combinator 更直接地使用 lambda 进行递归,但即使在那里,您也会将对自身的引用作为参数传递,除非优化器消除了递归,否则它可能会使用更多堆。 (经过高度调整的优化器可能会注意到可以消除无状态 lambda 引用参数,但这似乎很难解决)。
【讨论】:
AFAIK,std::function
是编写递归 lambda 的唯一方法。如果你能举个例子那就太好了!
std::function 调用有一个额外的参数:Effectively does INVOKE(f, std::forward<Args>(args)..., R)
(引用自en.cppreference.com/w/cpp/utility/functional/function/…)
@underscore_d 我也添加了结果。我还是不清楚。
@prak 链接到堆栈溢出时添加的 lambda 的许多 y 组合器之一。有很多变化。同样,不需要使用std::function
。 std::function
用于类型擦除,我们所做的任何事情都不需要类型擦除。无论如何你问为什么递归深度更短,现在应该清楚原因了吧?以上是关于C ++中lambda和常规函数的不同堆栈深度?的主要内容,如果未能解决你的问题,请参考以下文章