lambda 的深度递归

Deep recursion of lambda

本文关键字:递归 深度 lambda      更新时间:2023-10-16

>我有一个由N级循环组成的程序。但是N不是固定的,所以我拿模板来帮忙。以下是逻辑。

#include <type_traits>
void do_something1() {}
void do_something2() {}
enum Case { one, two };
struct loop {
    template<int N, Case case_, typename Lambda>
    std::enable_if_t < (0 < N) > func(Lambda&& visit) const {
        //return directly in some cases, so complexity is not a problem
        func<N - 1, one>([&visit] {
            do_something1();// depend on the results of func<N - 1, one>
            visit();
        });
        func<N - 1, two>([&visit] {
            do_something2();// depend on the results of func<N - 1, two>
            visit();
        });
    }
    template<int N, Case case_, typename Lambda>
    std::enable_if_t < (0 == N) > func(Lambda&& visit) const {
        visit();
    }
};
int main() {
    loop{}.func<32, one>([] {});
    return 0;
}

但是,由于深度递归,这不会编译。在我看来,它是内联和 func 中的两个分支导致问题。那么,有没有一种有效的方法来解决这个问题?

内联可以解决运行时的复杂性。"提前返回"还解决了运行时的复杂性。但是,编译器错误是因为编译时的复杂性。即使是无法访问的代码也必须编译。

如果编译器内联此代码,则此代码具有极大的复杂性。两个分支都将是内联的,这意味着f<31>// return early检查在f<32>中内联两次,f<30>// return early检查在f<31>中内联两次,因此在f<32>中内联4次,因此f<0>将内联约40亿次。没有像样的编译器应该有问题;它应该跳过内联。

简单的解决方案是删除Case(无论如何都不使用它)并N成为常规变量。更复杂的方法是在运行时检查变量 N 是否具有一些小的常量值 Nc,然后才调用函数的模板化版本。通过选择 Nc,可以限制模板实例化深度。