从C ++中的函数返回递归lambda

时间:2017-07-13 21:24:56

标签: c++ lambda

请参阅以下代码:

std::function<int(int)> makeFibonacci() {
    std::function<int(int)> fibonacci = [&fibonacci](int n) {
        if (n == 1) {
            return 1;
        }
        if (n == 2) {
            return 1;
        }
        return fibonacci(n-1) + fibonacci(n-2);
    };
    return fibonacci;
};

int main() {
    std::function<int(int)> fibonacci = makeFibonacci();
    std::cout << fibonacci(6) << std::endl;
    return 0;
}

当我运行它时,按预期输出数字8。但是,当我将捕获的&fibonacci更改为仅fibonacci进行副本捕获时,该程序实际上会在main的第一行{...}}上进行段错误。

如果makeFibonacci(第2行)是fibonacci函数的局部函数,因此当函数退出时超出范围,如何通过引用捕获它并递归使用?另外,为什么当我通过副本捕获lambda时程序会出现段错误?

4 个答案:

答案 0 :(得分:12)

  

如果fibonacci(第2行)是makeFibonacci()函数的局部函数,因此当函数退出时超出范围,如何通过引用捕获它并递归使用?

该功能正好按预期工作。你有什么是未定义的行为。您正在引用超出函数范围的对象。

  

另外,为什么当我通过副本捕获lambda时程序会出现段错误?

这是因为std::function的初始化方式。首先初始化lambda,然后用lambda初始化std::function。这意味着您正在复制未初始化的std::function实例,因此它可能不处于允许良好副本的状态。不变量在内部被破坏,这可能导致分段错误。

通过使用多态lambda,可以在没有std::function的情况下更高效地生成递归lambda函数,如下所示

auto makeFibonacci() {
    auto fib = [](int n, auto& self) {
        if (n == 1) {
            return 1;
        }
        if (n == 2) {
            return 1;
        }
        return self(n - 1, self) + self(n - 2, self);
    };
    return [fib](int n) {
        return fib(n, fib);
    };
};

lambda 拥有所需的所有状态。然后你可以像这样使用它

auto fibonacci = makeFibonacci();
cout << fibonacci(6) << endl;

另请注意,这是probably the worst way to calculate fibonacci numbers

答案 1 :(得分:2)

当您通过引用捕获时,您的程序具有未定义的行为,因为引用变为悬空。它恰好在您的情况下按预期工作,但这纯粹是偶然的。

当您更改为按副本捕获时,会出现段错误,因为在捕获时,fibonacci尚未构造,因此在捕获期间调用的复制构造函数正尝试从未初始化的对象进行复制:未定义的行为

我认为没有办法从函数返回一个递归的lambda(这样就不需要额外的参数)。 answer by @Curious显示了如何返回一个递归lambda,使用C ++ 14或更新版本。在C ++ 1中,如果你真的需要一个递归函子,你可以为它编写一个专用类。

旁注:在任何实际情况下,使用递归计算Fibonacci数几乎是不可能的,因为二次递归树快速增长。我知道这可能只是一个例子,但仍然。

答案 2 :(得分:0)

auto y_combinator=[](auto&&f){
  return [f=decltype(f)(f)](auto&&...args)->decltype(auto){
    return f(f,decltype(args)(args)...);
  };
};

std::function<int(int)> makeFibonacci() {
    auto fibonacci = [memo=std::map<int,int>{}](auto&& self, int n)mutable {
      if (n<3)
        return 1;
     auto it = memo.find(n);
     if (it != memo.end()) return *it;
     return memo[n]=(self(self,n-1)+self(self,n-2));
   };
  return y_combinator(fibonacci);
}

奖金备忘录。

答案 3 :(得分:0)

虽然效率不如其他方法,但std::function可用于返回递归lambda:

std::function<int(int)> makeFibonacci() {
    std::function<int(int)> fibonacci;
    return [fibonacci](int n) mutable {
        fibonacci = [&](int n) {
            if (n == 1) {
                return 1;
            }
            if (n == 2) {
                return 1;
            }
            return fibonacci(n-1) + fibonacci(n-2);
        };
        return fibonacci(n);
    };
};

但是它可以在C ++ 11中实现,并且不需要更改基础代码。