如何实现可返回的递归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++标准演进的历史原因:
- C++11最初引入lambda时,核心定位是简单的匿名函数工具,递归场景没有被纳入核心需求,委员会认为可以通过
std::function、外部引用等方式解决,不需要额外增加语法复杂度。 - lambda本质是编译器生成的匿名类的实例,在lambda定义时,这个匿名类的类型还没有完成声明,直接在lambda体内引用自身,相当于在类定义未完成时就引用类实例,语法实现存在障碍,直到C++23的显式对象参数提案解决了这个实现问题,才原生支持了lambda自引用。
内容的提问来源于stack exchange,提问作者Dmitriano
相关产品推荐
相关产品推荐

