C++;编译器能如此快速地计算递归constexpr函数吗? 我学习了C++ CONTXPRP函数,实现了 CONTXPRP递归函数,找到第n个斐波那契数。 #包括 #包括 #包括 #包括 #包括 constexpr long long fibonacci(int num){ if(num

C++;编译器能如此快速地计算递归constexpr函数吗? 我学习了C++ CONTXPRP函数,实现了 CONTXPRP递归函数,找到第n个斐波那契数。 #包括 #包括 #包括 #包括 #包括 constexpr long long fibonacci(int num){ if(num,c++,function,recursion,constexpr,C++,Function,Recursion,Constexpr,constepr函数没有副作用,因此可以毫无顾虑地进行记忆。考虑到运行时的差异,最简单的解释是编译器在编译时记忆constepr函数。这意味着fibonacci(n)只对每个n计算一次,所有其他递归调用都从查找表返回。要为其他人指出的内容添加一些细节:constexpr函数不必在运行时计算,可能影响它的参数之一是-fconstexpr ops limit 在GCC 10.2.0中,-fconstexpr ops limit=100000000(1B)和fibonacci(40)会产生一个预编译的

constepr
函数没有副作用,因此可以毫无顾虑地进行记忆。考虑到运行时的差异,最简单的解释是编译器在编译时记忆constepr函数。这意味着
fibonacci(n)
只对每个
n
计算一次,所有其他递归调用都从查找表返回。

要为其他人指出的内容添加一些细节:
constexpr
函数不必在运行时计算,可能影响它的参数之一是
-fconstexpr ops limit

在GCC 10.2.0中,
-fconstexpr ops limit=100000000
(1B)和
fibonacci(40)
会产生一个预编译的值,但如果您将限制降低到10000000(10M)然后在运行时计算函数。如果要确保始终在编译时计算该值,除了
fibonacci
函数外,还需要将
long-long-num
标记为
constepr


注意:另一个例子是编译时计算的非constexpr函数(优化输出),并用
\uuuu属性(const))标记它
可能有助于编译器做出这样的决定。但是,我的编译器没有对其进行优化。

在g++8.3.0中,如果在这种情况下使用
constepr
,它会计算您正在使用的值并将结果作为常量输出。即使没有优化,这也是如此:

//#include <iostream>

constexpr long long fibonacci(int num){
    if(num <= 2){return 1;}
    return fibonacci(num - 1) + fibonacci(num - 2);
}

int main(int argc, char** argv)
{

    //double start = clock();
    long long num = fibonacci(70);
    //std::cout << num << std::endl;
    //cout << (clock()-start)/(CLOCKS_PER_SEC/1000) << endl;

    return 0;
}
我想知道为什么代码和编译器在执行时间上有如此巨大的差异

它似乎没有递归就计算出来了,但是递归太慢了


让我惊讶的是,它可以在编译时将递归函数转换为迭代函数,即使没有优化。至少看起来是这样。

哪种编译器?当使用
-std=c++17
(带或不带
-O3
)编译时,godbolt在使用GCC 10.2和clang 11.0的代码上超时。两位编译器都认为
constepr
递归太深,编译后在运行时进行计算,然后在运行时超时。蛮力斐波那契的速度非常慢。编译器找到了一种更好的方法。猜测一下——识别递归的等效迭代形式,并结合记忆。但最终,它将取决于qua编译器实现的复杂性。@bloody:Sigh…“error”意味着有一个正确的答案。这并不排除其他编译器、标志等可能工作的可能性,但操作需要明确。如果我使
long long num=fibonacci(70);
been
constexpr long num=fibonacci(70)
为了强制进行编译时计算,他们都死了,抱怨递归过度。这就是为什么我问OP是如何编译他们的代码的;我不认为他们错了,但需要重现他们的结果。(离题吹毛求疵:如果您关心性能,就不会使用
std::endl
强制在行之间对cout进行无意义的刷新,即使输出是管道也是如此。)@ahskdjfk正确。这是一个优化,constexpr允许,仅仅是因为它的性质。我不相信编译器需要做这个优化,但是,你的里程可能会有所不同。这可能是一个游戏。编译器可以对你的代码做任何它想做的事情,如果它认为它会更快,并且不会改变可观察的行为我们的.没有理由编译器在计算
constxepr
函数的结果时不能在内部执行此操作。有趣的问题是,如果编译器可以在编译时看到
constexpr
函数的这种优化,为什么不将其应用于运行时版本?@orlp:正如我在上面的评论中提到的,不同版本的gcc绝对不会应用这种优化(OP的7.5会,最新的10.2版本不会,至少在默认情况下不会),所以是的,绝对是一种里程变化的情况。@user4581301-许多可能的解释解释解释了为什么编译器会在编译时“看到”对
constepr
函数的优化,而不是对“运行时版本”。
constexpr
允许编译器进行假设,但必须进行分析,以得出“运行时”版本的结论。该分析可能无法完成(可能会很昂贵),因此优化没有完成。相反,编译器可能会在尝试计算
constepr
函数时疾驰而过,直到它耗尽内存。编译器实现的质量取决于许多因素。我不喜欢我的编译器在运行时引入一个不可见的、不可控的哈希表来记忆我的函数。Memo编译时的ry是“便宜的”"注意:如果您使用
constepr long long num
,这将保证它不会在运行时被计算。它不保证它会在编译时被计算;它只是确保当编译器可能返回到运行时计算时,编译会失败取而代之的是s。
        .file   "constexpr.cc"
        .text
        .globl  main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -20(%rbp)
        movq    %rsi, -32(%rbp)
        movabsq $190392490709135, %rax
        movq    %rax, -8(%rbp)
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .ident  "GCC: (Debian 8.3.0-6) 8.3.0"
        .section        .note.GNU-stack,"",@progbits