我发现了这段代码,并想知道我是否真的应该在我的真实项目中实现这样的东西。
令我困惑的是
这需要更多的编译时间,但我不应该费心 以运行时为代价编译时间。
如果N真的是一个非常大的数字呢?有文件吗? 源代码中的大小限制?
还是要了解的事情,不要实施?
#include <iostream>
using namespace std;
template<int N>
class Factorial {
public:
static const int value = N * Factorial<N-1>::value;
};
template <>
class Factorial<1> {
public:
static const int value = 1;
};
int main() {
Factorial<5L> f;
cout << "5! = " << f.value << endl;
}
输出:
5! = 120
稍有修改,因为我正在玩代码,发现
Factorial<12> f1; // works
Factorial<13> f2; // doesn't work
错误:
undefined reference to `Factorial<13>::value'
是不是可以进一步升级到12
深度?
答案 0 :(得分:5)
1
的答案取决于它,template meta programming实质上涉及在编译时进行计算与在运行时不必进行计算之间的权衡。 。通常,这种技术可能导致难以阅读和维护代码。因此,答案最终取决于您需要在较慢的编译时间内获得更快的运行时性能,并且可能更难维护代码。
文章Want speed? Use constexpr meta-programming! 解释了在现代C ++中,您可以多次使用constexpr functions作为模板元编程的替代品。这通常会导致代码更易读,也许更快。将此模板元编程方法与constexpr示例进行比较:
constexpr int factorial( int n )
{
return ( n == 0 ? 1 : n*factorial(n-1) ) ;
}
对于常量表达式的参数,更简洁,可读并且将被执行at compile time尽管链接的答案解释了标准实际上并不是说它必须但实际上是当前的实现肯定。
值得注意的是,由于结果会快速溢出value
constexpr 的另一个优点是undefined behavior is not a valid constant expression,至少gcc
的当前实现在大多数情况下,clang
会将 constexpr 中的未定义行为转换为错误。例如:
constexpr int n = factorial(13) ;
对我来说会产生以下错误:
error: constexpr variable 'n' must be initialized by a constant expression
constexpr int n = factorial(13) ;
^ ~~~~~~~~~~~~~
note: value 6227020800 is outside the range of representable values of type 'int'
return ( n == 0 ? 1 : n*factorial(n-1) ) ;
^
这也是你举例的原因:
Factorial<13> f2;
失败,因为需要常量表达式并且gcc 4.9
提供了有用的错误:
error: overflow in constant expression [-fpermissive]
static const int value = N * Factorial<N-1>::value;
^
虽然gcc
的旧版本为您提供了一个不太有用的错误。
对于问题2
,编译器有一个模板递归限制,通常可以配置,但最终会耗尽系统资源。例如,gcc
的标记为-ftemplate-depth=n:
将模板类的最大实例化深度设置为n。一个限制 在模板上实例化深度需要检测无穷无尽 模板类实例化期间的递归。 ANSI / ISO C ++ 符合规定的程序不得依赖于大于17的最大深度 (在C ++ 11中更改为1024)。作为编译器,默认值为900 在某些情况下,在达到1024之前可能会耗尽堆栈空间。
在您的特定问题中,您需要担心有问题的整数溢出,这是在您遇到系统资源问题之前的未定义行为。