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 区分了两种失败:递归深度和“步数限制”。
“clang 最大步数限制”的 Google 搜索结果显示有关实现此功能的 clang 补丁的页面,包括命令行选项的实现:-fconstexpr-steps
。进一步谷歌搜索此选项表明没有用户级文档。
因此,没有关于 clang 算作“步骤”或 clang 需要多少“步骤”的文档fib(27)
。我们可以把这个值设置得很高,但我认为这是一个坏主意。相反,一些实验表明:
n : steps
0 : 2
1 : 2
2 : 6
3 : 10
4 : 18
这表明步骤(fib(n)
) == 步骤(fib(n-1)
) + 步骤(fib(n-2)
) + 2. 稍微计算一下,根据这个,fib(27)
应该需要 1,271,242 个 clang 步数。所以编译用-fconstexpr-steps=1271242
应该允许程序编译,这确实it does http://coliru.stacked-crooked.com/a/baf8408fad71adce。编译用-fconstexpr-steps=1271241
会产生与之前相同的错误,因此我们知道我们有一个确切的限制。
An alternative, less exact method involves observing from the patch that the default step limit is 1,048,576 (220), which is obviously sufficient for fib(26)
. Intuitively, doubling that should be plenty, and from the earlier analysis we know that two million is plenty. A tight limit would be ⌈φ · steps(fib(26)
)⌉ (which does happen to be exactly 1,271,242).
另一件需要注意的事情是,这些结果清楚地表明 clang 没有对 constexpr 评估进行任何记忆化。海湾合作委员会does https://gcc.gnu.org/ml/gcc-patches/2009-11/msg01504.html,但似乎这根本没有在 clang 中实现。尽管记忆会增加内存需求,但有时(如本例所示)可以大大减少评估所需的时间。我从中得出的两个结论是,编写需要记忆以获得良好编译时间的 constexpr 代码对于可移植代码来说并不是一个好主意,并且可以通过支持 constexpr 记忆和启用/禁用它的命令行选项来改进 clang。