模板作为递归函数工作

Rup*_*av. 1 c++ templates

我找到了这段代码,并想知道我是否真的应该在我的真实项目中实现这样的东西.

令我困惑的事情是

  1. 这需要更多的编译时间,但我不应该以运行时为代价来打扰编译时间.

  2. 如果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;

    }
Run Code Online (Sandbox Code Playgroud)

输出:

5! = 120
Run Code Online (Sandbox Code Playgroud)

稍微修改一下,因为我正在玩代码,发现了

  Factorial<12> f1;  // works  
  Factorial<13> f2;  // doesn't work
Run Code Online (Sandbox Code Playgroud)

错误:

undefined reference to `Factorial<13>::value'
Run Code Online (Sandbox Code Playgroud)

是不是可以12进一步深入?

Sha*_*our 5

答案1是,它取决于,模板元编程实质上涉及在编译时进行计算与在运行时不必进行计算之间的权衡.通常,这种技术可能导致难以阅读和维护代码.因此,答案最终取决于您需要在较慢的编译时间内获得更快的运行时性能,并且可能更难维护代码.

想要速度的文章使用constexpr元编程!解释了如何在现代C++中多次使用constexpr函数作为模板元编程的替代品.这通常会导致代码更易读,也许更快.将此模板元编程方法与constexpr示例进行比较:

constexpr int factorial( int n )
{
   return ( n == 0 ? 1 : n*factorial(n-1) ) ;
}
Run Code Online (Sandbox Code Playgroud)

它更简洁,可读,并且将在编译时常量表达式的参数执行,尽管链接的答案解释了标准实际上并不是说它必须是,但实际上当前的实现肯定会这样做.

值得注意的是,由于结果将很快溢出constexpr的value另一个优点是未定义的行为不是有效的常量表达式,并且至少当前的实现并且将在constexpr中将未定义的行为转换为大多数情况下的错误.例如:gccclang

constexpr int n = factorial(13) ;
Run Code Online (Sandbox Code Playgroud)

对我来说会产生以下错误:

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) ) ;
                       ^
Run Code Online (Sandbox Code Playgroud)

这也是你举例的原因:

Factorial<13> f2;
Run Code Online (Sandbox Code Playgroud)

失败,因为需要一个常量表达式gcc 4.9给出一个有用的错误:

error: overflow in constant expression [-fpermissive]
 static const int value = N * Factorial<N-1>::value;
                  ^
Run Code Online (Sandbox Code Playgroud)

虽然旧版本gcc给你的是你看到的不太有用的错误.

对于问题2,编译器有一个模板递归限制,通常可以配置,但最终你将耗尽系统资源.例如,对于所述标志gcc将是-ftemplate深入= N:

将模板类的最大实例化深度设置为n.在模板类实例化期间,需要对模板实例化深度进行限制以检测无限递归.符合ANSI/ISO C++标准的程序不得依赖于大于17的最大深度(在C++ 11中更改为1024).默认值为900,因为在某些情况下编译器在达到1024之前可能会耗尽堆栈空间.

在您的特定问题中,您需要担心有问题的整数溢出,这是在您遇到系统资源问题之前的未定义行为.