无论如何,有没有配置constexpr实例化深度?
我正在使用-fconstexpr-depth = 4096(使用clang/XCode)运行。

但是仍然无法编译此代码并出现错误:
Constexpr变量fib_1必须通过常量表达式初始化。
无论是否设置了选项-fconstexpr-depth = 4096,该代码都会失败。

这是clang的错误,还是预期是这种方式。
注意:直到fib_cxpr(26),27开始失败时,此方法才有效。

码:

constexpr int fib_cxpr(int idx) {
    return idx == 0 ? 0 :
           idx == 1 ? 1 :
           fib_cxpr(idx-1) + fib_cxpr(idx-2);
}

int main() {
    constexpr auto fib_1 = fib_cxpr(27);
    return 0;
}

最佳答案

TL; DR:

对于clang,您需要命令行参数-fconstexpr-steps=1271242,而您只需要-fconstexpr-depth=27
计算斐波那契数的递归方法不需要很大的递归深度。实际上fib(n)所需的深度不超过n。这是因为最长的调用链是通过fib(i-1)递归调用。

constexpr auto fib_1 = fib_cxpr(3); // fails with -fconstexpr-depth=2, works with -fconstexpr-depth=3
constexpr auto fib_1 = fib_cxpr(4); // fails with -fconstexpr-depth=3, works with -fconstexpr-depth=4

因此,我们可以得出结论,-fconstexpr-depth与设置无关紧要。

此外,错误消息还表明存在差异:
constexpr auto fib_1 = fib_cxpr(27);

为了确保我们达到该限制,使用-fconstexpr-depth=26进行了编译,clang生成了以下消息:
note: constexpr evaluation exceeded maximum depth of 26 calls

但是用足够深的-fconstexpr-depth=27编译会产生以下消息:
note: constexpr evaluation hit maximum step limit; possible infinite loop?

因此,我们知道clang区分了两种失败:递归深度和“步长限制”。

Google对于“最大最大步长限制”的最高搜索结果指向有关实现此功能的catch补丁的页面,包括命令行选项-fconstexpr-steps的实现。进一步搜索该选项表明没有用户级文档。

因此,没有文档说明fib(27)将哪些clang视为一个“步骤”或多少个“steps”。我们可以将其设置得很高,但是我认为这是一个坏主意。相反,一些实验表明:
n : steps
0 : 2
1 : 2
2 : 6
3 : 10
4 : 18

这表示step(fib(n))== steps(fib(n-1))+ steps(fib(n-2))+2。根据一点计算,得出fib(27)应该需要1,271,242个clang步骤。因此,使用-fconstexpr-steps=1271242进行编译应该允许程序进行编译,而实际上是it does。使用-fconstexpr-steps=1271241进行编译会导致与以前相同的错误,因此我们知道我们有一个确切的限制。

另一种不太精确的方法涉及从补丁中观察到默认步长限制为1,048,576(220),这对于fib(26)显然足够了。直观地讲,加倍应该足够了,从早期的分析中我们知道200万已经足够了。一个严格的限制是⌈φ·steps(fib(26))⌉(恰好恰好是1,271,242)。

要注意的另一件事是,这些结果清楚地表明clang并没有做任何constexpr评估的记录。 GCC does,但似乎根本没有用clang实现。尽管备忘录增加了内存要求,但有时在这种情况下可以极大地减少评估所需的时间。我从中得出的两个结论是,编写需要使用备忘录以实现良好编译时间的constexpr代码对于可移植代码而言不是一个好主意,并且可以通过支持constexpr备忘录和启用/禁用它的命令行选项来改进clang。

07-24 09:46
查看更多