无论如何,有没有配置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。