[英]Call recursive function in std::function
I just wrote a thread pool using C++11/14 std::thread objects and use tasks in the worker queue. 我只是使用C ++ 11/14 std :: thread对象编写了一个线程池,并在worker队列中使用了任务。 I encountered some weird behaviour when calling recursive functions in lambda expressions.
在lambda表达式中调用递归函数时,我遇到了一些奇怪的行为。 The following code crashes if you implement
fac()
in a recursive fashion (both with clang 3.5 and gcc 4.9): 如果您以递归方式(同时使用clang 3.5和gcc 4.9)实现
fac()
,则以下代码会崩溃:
#include <functional>
#include <vector>
std::size_t fac(std::size_t x) {
// This will crash (segfault).
// if (x == 1) return 1;
// else return fac(x-1)*x;
// This, however, works fine.
auto res = 1;
for (auto i = 2; i < x; ++i) {
res *= x;
}
return res;
}
int main() {
std::vector<std::function<void()> > functions;
for (auto i = 0; i < 10; ++i) {
functions.emplace_back([i]() { fac(i); });
}
for (auto& fn : functions) {
fn();
}
return 0;
}
It does, however, work fine with the iterative version above. 但是,它确实可以与上述迭代版本配合使用。 What am I missing?
我想念什么?
for (auto i = 0; i < 10; ++i) {
functions.emplace_back([i]() { fac(i); });
The first time through that loop, i
is going to be set to zero, so you're executing: 第一次通过该循环,
i
将被设置为零,因此您正在执行:
fac(0);
Doing so with the recursive definition: 使用递归定义来执行此操作:
if (x == 1) return 1;
else return fac(x-1)*x;
means that the else
block will execute, and hence x
will wrap around to whatever the maximum size_t
value is (as it's unsigned). 表示将执行
else
块,因此x
将回绕到size_t
的最大值(因为它是无符号的)。
Then it's going to run from there down to 1
, consuming one stack frame each time. 然后它将从那里向下运行到
1
,每次消耗一个堆栈帧。 At a minimum, that's going to consume 65,000 or so stack frames (based on the minimum allowed value of size_t
from the standards), but probably more, much more. 至少,那将消耗65000左右栈帧(根据允许的最小值
size_t
从标准),但可能更多, 更多 。
That's what causing your crash. 这就是导致您崩溃的原因。 The fix is relatively simple.
解决方法相对简单。 Since
0!
从
0!
is defined as 1
, you can simply change your statement to be: 被定义为
1
,您可以简单地将语句更改为:
if (x <= 1)
return 1;
return fac (x-1) * x;
But you should keep in mind that recursive functions are best suited to those cases where the solution space reduces quickly, a classic example being the binary search, where the solution space is halved every time you recur. 但是,请记住,递归函数最适合解决方案空间快速减少的情况,经典的例子是二进制搜索,其中每次递归时解决方案空间减半。
Functions that don't reduce solution space quickly are usually prone to stack overflow problems (unless the optimiser can optimise away the recursion). 不能迅速减少解决方案空间的函数通常容易出现堆栈溢出问题(除非优化器可以优化递归)。 You may still run into problems if you pass in a big enough number and it's no real different to adding together two unsigned numbers with the bizarre (though I actually saw it put forward as a recursive example many moons ago):
如果传递的数字足够大,您可能仍然会遇到问题,并且将两个无符号的数字与奇异值相加并没有什么真正的区别(尽管我实际上看到它是许多月前作为递归示例提出的):
def addu (unsigned a, b):
if b == 0:
return a
return addu (a + 1, b - 1)
So, in your case, I'd stick with the iterative solution, albeit making it bug-free: 因此,在您的情况下,我会坚持使用迭代解决方案,尽管使其没有错误:
auto res = 1;
for (auto i = 2; i <= x; ++i) // include the limit with <=.
res *= i; // multiply by i, not x.
Both definitions have different behavior for x=0
. 对于
x=0
两个定义都有不同的行为。 The loop will be fine as it uses the less-than operator: 循环将很好,因为它使用了小于运算符:
auto res = 1;
for (auto i = 2; i < x; ++i) {
res *= x;
}
However, 然而,
if (x == 1) return 1;
else return fac(x-1)*x;
Results in a quasi-infinite loop as x == 1
is false and x-1
yields the largest possible value of std::size_t
(typically 2 64 -1). 当
x == 1
为false且x-1
产生std::size_t
的最大可能值(通常为2 64 -1)时,将导致拟无限循环。
The recursive version does not take care of the case for x == 0
. 递归版本不考虑
x == 0
的情况。
You need: 你需要:
std::size_t fac(std::size_t x) {
if (x == 1 || x == 0 ) return 1;
return fac(x-1)*x;
}
or 要么
std::size_t fac(std::size_t x) {
if (x == 0 ) return 1;
return fac(x-1)*x;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.