c++ - 通过 lambda 定义的 constexpr 递归函数

标签 c++ lambda c++17

是否有一种方便的方法来使用 lambda 语法定义递归 constexpr 函数?我发现了一种不方便的方法,即通过分配给 constexpr 函数指针来完成此操作,但我希望有一种方法可以减少输入次数,并且无需更改该函数的类型 lambda 。

以普通方式创建递归 constexpr 函数非常简单。 特别是,自 C++11 起就支持使用可能包含三元运算符的单个表达式。

constexpr double factorial2(double x) {
    return (x < 0) ? 0 : \
           (x == 0) ? 1 : \
           /*otherwise*/ (x * factorial2(-1 + x));
}

目前尚不清楚如何使用 lambda 语法来执行此操作。我将在下面列出我的各种失败的尝试,但我找到了一种通过使用 constexpr 函数指针而不是 auto 作为变量的类型注释来创建 constexpr 函数的方法我正在使用 lambda 进行初始化。

typedef double (* factorial_t)(double);

constexpr factorial_t factorial = [](double x) constexpr noexcept -> double {
    return (x < 0) ? 0 : \
           (x == 0) ? 1 : \
           /*otherwise*/ (x * factorial(-1 + x));
};

Clang 会接受这一点,godbolt 上的 GCC 9.2 也会接受。

// foo.cpp
#include <cstdio>

typedef double (* factorial_t)(double);

constexpr factorial_t factorial = [](double x) constexpr noexcept -> double {
    return (x < 0) ? 0 : \
           (x == 0) ? 1 : \
           /*otherwise*/ (x * factorial(-1 + x));
};


int main() {
    constexpr auto x{factorial(27)};
    printf("%f\n", x);
}

并运行它:

$ rm -f ./a.out && clang++-7 -std=c++17 foo.cpp && ./a.out
10888869450418351940239884288.000000
<小时/>

本节只是一个附录,解释了为什么我决定使用函数指针而不是其他东西。

尝试通过 lambda 生成递归 constexpr 函数失败。

1) 使用自动

如上所述in this somewhat old question ,使用您在 lambda 中定义的事物的名称是允许的,但与类型推断不能很好地交互。建议使用 std::function

的答案
auto factorial = [](double x) constexpr noexcept -> double {
    return (x < 0) ? 0 : \
           (x == 0) ? 1 : \
           /*otherwise*/ (x * factorial(-1 + x));
};

错误:

bar.cpp:7:31: error: variable 'factorial' declared with deduced type 'auto' cannot appear in its own initializer
           /*otherwise*/ (x * factorial(-1 + x));

2) 使用std::function

这不起作用,因为 std::function 是非文字类型。显然。

// bar.cpp
#include <cstdio>
#include <functional>

constexpr std::function<double(double)> factorial = [](double x) constexpr noexcept -> double {
    return (x < 0) ? 0 : \
           (x == 0) ? 1 : \
           /*otherwise*/ (x * factorial(-1 + x));
};


int main() {
    constexpr auto x{factorial(27)};
    printf("%f\n", x);
}

失败并显示错误消息:

bar.cpp:5:41: error: constexpr variable cannot have non-literal type 'const std::function<double (double)>'
constexpr std::function<double(double)> factorial = [](double x) constexpr noexcept -> double {
                                        ^
    /usr/bin/../lib/gcc/aarch64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/functional:1834:11: note: 'function<double (double)>' is
          not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors
        class function<_Res(_ArgTypes...)>

最佳答案

由于 blog post by Pedro Melendez 有一个技巧,它绕过了直接递归,并且可以在 constexpr 上下文中使用。感谢@HolbyBlackCat 的reference和想法。

constexpr auto factorial = [](int n) {
    auto factorial_impl = [](int n, auto& factorial_ref) {
        if(n <= 1) { return 1; }
        return n * factorial_ref(n-1, factorial_ref);
    };
    return factorial_impl(n,factorial_impl);
};

See it on GodBolt .

(外部)lambda 是一种“闭包类型”,它变成了“文字”并且只能在 C++17 中与 constexpr 一起使用(因此这不适用于 C++14) .

PS - 我稍微简化了你的阶乘函数并使用整数,因为恕我直言,使用 double 只会分散对问题的注意力。

关于c++ - 通过 lambda 定义的 constexpr 递归函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59523991/

相关文章:

c++ - 用于打印数组的 toString 不工作 C++

C# List<T> lambda 查找然后修改元素

java - 如何更新 lambda 函数外部声明的变量的值?

c++ - 如果不先初始化 vector ,该如何处理 vector ?

c++ - 直接列表初始化和复制列表初始化之间的区别

c++ - 在 qi spirit 中回滚替代解析器的变化

c++ - 构造函数设置?

c++ - std::optional has_value() 怎么可能是 constexpr?

C++17:编译器支持 pmr 命名空间类

c++ - 模仿C++中的fortran print and write语法