You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可返回的递归lambda并修复悬空引用问题

问题解答

根因说明

你原代码的核心问题是:func是make_lambda函数内的局部对象,lambda通过[&func]按引用捕获后,函数返回时局部func会被销毁,lambda内部持有的引用变成悬空引用,调用时会触发未定义行为。

以下所有方案均不需要std::shared_ptr<std::function<int(int)>>,也不使用传引用输出参数,完全符合你的要求。

最优修复方案

方案1:C++23 显式对象参数(deducing this)

这是目前开销最低、代码最简洁的方案,没有任何动态内存分配和类型擦除开销
C++23引入的显式对象参数特性,允许lambda直接声明自身作为第一个参数,天然支持自引用,不需要依赖任何外部包装:

#include <iostream>

auto make_lambda()
{
    return [](this auto&& self, int val) -> int
    {
        if (val < 10)
        {
            return self(val + 1);
        }
        return val;
    };
}

int main()
{
    std::cout << make_lambda()(0);
    return 0;
}

方案2:C++14 移动捕获std::function

如果无法使用C++23,可以通过移动捕获把std::function存到lambda内部,避免引用局部对象:

#include <iostream>
#include <functional>
#include <utility>

std::function<int(int)> make_lambda()
{
    std::function<int(int)> func;
    func = [func = std::move(func)](int val) mutable
    {
        if (val < 10)
        {
            return func(val + 1);
        }
        return val;
    };
    return func;
}

int main()
{
    std::cout << make_lambda()(0);
    return 0;
}

这个方案只有std::function本身的少量类型擦除开销,比共享指针方案轻量很多。

方案3:Y组合子(兼容C++11及以上,无类型擦除开销)

如果不想用std::function,可以自己实现一个极简的Y组合子,完全在编译期生成代码,没有动态内存开销:

#include <iostream>
#include <utility>

template <typename F>
struct YCombinator {
    F f;
    template <typename... Args>
    decltype(auto) operator()(Args&&... args) const {
        return f(*this, std::forward<Args>(args)...);
    }
};

template <typename F>
YCombinator(F) -> YCombinator<F>;

auto make_lambda()
{
    return YCombinator{[](auto&& self, int val) -> int {
        if (val < 10) {
            return self(val + 1);
        }
        return val;
    }};
}

int main()
{
    std::cout << make_lambda()(0);
    return 0;
}

补充问题解答:为什么lambda没有内置类似this的自引用语法?

这是C++标准演进的历史原因:

  1. C++11最初引入lambda时,核心定位是简单的匿名函数工具,递归场景没有被纳入核心需求,委员会认为可以通过std::function、外部引用等方式解决,不需要额外增加语法复杂度。
  2. lambda本质是编译器生成的匿名类的实例,在lambda定义时,这个匿名类的类型还没有完成声明,直接在lambda体内引用自身,相当于在类定义未完成时就引用类实例,语法实现存在障碍,直到C++23的显式对象参数提案解决了这个实现问题,才原生支持了lambda自引用。

内容的提问来源于stack exchange,提问作者Dmitriano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 07:00:04