我想知道下一个代码。
#include <iostream>
using std::cout;
template<int N> struct Fib {
enum { v = Fib<N - 1>::v + Fib<N - 2>::v };
};
template<> struct Fib<0> {
enum { v = 0 };
};
template<> struct Fib<1> {
enum { v = 1 };
};
int fib(int n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
int main() {
cout << Fib<46>::v << '\n';
// cout << fib(46) << '\n';
return 0;
}
它在编译时计算结果,没有任何明显的延迟。这怎么可能?如果我们使用对fib(46)的调用,我们将不得不等待几秒钟,即使使用最快的PC。该模板使用相同的计算模式,并立即生成。我也对使用模板生成的可执行文件的大小几乎与没有模板的大小相同这一事实感到惊讶。我用了GCC。
答案 0 :(得分:17)
这是由于模板解决方案中固有的 memoization 。
在编译期间,每个实例化(如Fib<1>
,Fib<2>
等)仅由(由编译器)执行一次并记住。
另一方面,当您运行fib(n)
时,会计算fib(1)
,fib(2)
等多次。解决方案可能是 memoize 它,即记住地图或数组中每个fib
调用的结果,如果结果已经存在,则返回该结果。
答案 1 :(得分:1)
他们并不快,他们已经在那里了。
如果您设法编写这样的模板程序,那么您在程序启动之前将使用您正在使用的值
这也可以通过constexpr
来实现
但是,在编译时需要所有信息的事实就是这样
适用于极少数用例。
我重写了你的例子,向你展示(link to example)。
main:
.LFB0:
.file 1 "/tmp/compiler-explorer-compiler118417-63-1cf1gj5.e1tp/example.cpp"
.loc 1 12 0
.cfi_startproc
.loc 1 14 0
mov eax, 1836311903
ret
eax
填充的数字1836311903
恰好是第46个斐波纳西数。
答案 2 :(得分:0)
fib()
是一个递归函数,在这种情况下对性能有害,因为函数调用和堆栈创建的多次迭代。
模板值将静态计算值,因为N在编译时是已知的。还试试:
constexpr int fib(int n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
这将使其等效于模板解决方案,如果可能,将在编译时计算函数值) 与-O2标志:P