为什么 clang 不使用斐波那契的 constexpr 版本计算斐波那契(500)



我正在尝试用constexpr

#include <iostream>
constexpr long long fibonacci(const int x)
{
    return x <= 1 ? 1 : fibonacci(x - 1) + fibonacci(x - 2);
}
int main()
{
    const long long lol = fibonacci(500);
    std::cout << lol << std::endl;
}

所以我希望lol编译时计算

toogy@stewie
» g++ -std=c++14 -g src/test.cc -o test.out
toogy@stewie
» ./test.out 
4859788740867454402

它与g++配合得很好。在编译时,它甚至会做一些记忆,优化这个蹩脚的斐波那契函数,然后立即计算fibonacci(500)

然后我尝试clang++

toogy@stewie
» clang++ -std=c++1y -g src/test.cc -o test.out 
toogy@stewie
» ./test.out
... very long

lol不是在编译时由clang++计算的(由gdb证明)。为什么?

它命中 clang 的最大递归深度。你可以通过constexpr来强制lol在编译时被计算,即:

constexpr long long lol = fibonacci(500);

这样做并使用clang++ -std=c++11 t.cpp进行编译会产生错误:

t.cpp:10:25: error: constexpr variable 'lol' must be initialized by a constant
      expression
    constexpr long long lol = fib(500);
                        ^     ~~~~~~~~
t.cpp:4:1: note: constexpr evaluation hit maximum step limit; possible infinite
      loop?
{
^
t.cpp:5:38: note: in call to 'fib(4)'
    return x <= 1 ? 1 : fib(x - 1) + fib(x - 2);
                                     ^
t.cpp:5:25: note: in call to 'fib(6)'
    return x <= 1 ? 1 : fib(x - 1) + fib(x - 2);
                        ^
t.cpp:5:38: note: in call to 'fib(7)'
    return x <= 1 ? 1 : fib(x - 1) + fib(x - 2);
                                     ^
t.cpp:5:25: note: in call to 'fib(9)'
    return x <= 1 ? 1 : fib(x - 1) + fib(x - 2);
                        ^
t.cpp:5:25: note: in call to 'fib(10)'
t.cpp:5:25: note: (skipping 480 calls in backtrace; use
      -fconstexpr-backtrace-limit=0 to see all)
t.cpp:5:25: note: in call to 'fib(496)'
t.cpp:5:25: note: in call to 'fib(497)'
t.cpp:5:25: note: in call to 'fib(498)'
t.cpp:5:25: note: in call to 'fib(499)'
t.cpp:10:31: note: in call to 'fib(500)'
    constexpr long long lol = fib(500);
                              ^
1 error generated.

Clang不能(使用默认编译器标志;尽管我仍然无法让它用-fconstexpr-depth=1000000000(即10亿))在编译时进行fibonacci(500),因此它会在运行时使用您发布的代码进行计算。正如@Streppel链接一样,您可以使用 -fconstexpr-depth=N 编译器标志增加常量表达式的最大递归深度。

然而,第 500 个斐波那契数是巨大的*,所以这肯定会溢出,这是有符号整数的未定义行为(所以所有的赌注都是关闭的,真的)。(但是如果你使用模板元编程,你可以做到这一点)

*如 105 位数字巨大:139423224561697880139724382870407283950070256587697307264108962948325571622863290691557658876222521294125

相关内容

最新更新