为什么在运行时而不是在编译时使用constexpr初始化变量
据我了解,关键字constexpr告诉编译器表达式的计算可以在编译时发生。具体来说,constexpr在变量上意味着可以在编译时评估变量的值,而constexpr在函数上意味着可以在编译时调用该函数并评估其返回值。如果函数在运行时被调用,它只是作为一个普通函数。
今天,我写了一段代码来尝试使用constexpr:
#include <iostream>
using namespace std;
constexpr long int fib(int n)
{
return (n <= 1)? n : fib(n-1) + fib(n-2);
}
int main ()
{
constexpr long int res = fib(32);
// const long int res = fib(32);
cout << res << endl;
return 0;
}
我原以为代码的编译会花费很多时间,但我错了。编译只用了0.0290s:
$ time g++ test.cpp
real 0m0.290s
user 0m0.252s
sys 0m0.035s
但是如果我constexpr long int res = fib(32);换成const long int res = fib(32);,出乎我的意料,它在编译上花费了更多的时间:
$ time g++ test.cpp
real 0m5.830s
user 0m5.568s
sys 0m0.233s
总之,这似乎 const使函数fib(32)在编译时被评估,但constexpr让它在运行时被评估。我真的很困惑。
我的系统:Ubuntu 18.04
我的 gcc: g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
回答
通过检查生成的程序集,我们可以确认G++ 7.5在两种情况下都fib(32)在编译时计算了值:
movl $2178309, %esi
G++constexpr如此快速地评估上下文的原因是它在评估和上下文时执行的记忆。constexprtemplate
记忆化通过将其降低到 O(N) 复杂度来完全消除斐波那契计算复杂度。
那么为什么非constexpr评估会慢这么多呢?我认为这是优化器中的错误/缺点。如果我尝试使用 G++ 8.1 或更高版本,编译时间没有区别,所以大概已经解决了。