模板元编程的递归限制

C++的模板元编程是C++比较高级的一种技术,可以通过一些模板的技巧,来实现编译期的计算,常见的例子如求个和啊,求个Fibonacci数列啊啥的。说实话,我从没在实际项目中用过这些技术,昨天和同事聊到C++,Java以及C#的模板时,突然想到这个,就试了一下,就写了个求和的:

#include <iostream>

using namespace std;

template<int N>
class Sum
{
    public:
        enum {value = N + Sum<N-1>::value };
};

template<>
class Sum<0>
{
    public:
        enum {value = 0};
};


int main()
{
    cout << Sum<100>::value << endl;
}

无非也就是个递归,就像写普通的递归函数一样,写上递归规律(模板函数)和递归结束条件(模板函数特化),但当我把传入的值换成501时(gcc4.1?),编译器就报错了,从出错信息判断貌似编译器对模板递归有限制,后来在gcc 4.6上试了一下,他的上限是1024,而且错误信息比较靠谱:

sum.cpp:9:14: error: template instantiation depth exceeds maximum of 1024 (use -ftemplate-depth= to increase the maximum) instantiating ‘class Sum<1>’ sum.cpp:9:14: recursively instantiated from ‘Sum<1024>’ sum.cpp:9:14: instantiated from ‘Sum<1025>’ sum.cpp:22:22: instantiated from here

那就用-ftemplate-depth吧,当然能增大,但是毕竟系统资源有限,你不能无节制的往上加。编译器有此限制当然有其原因:

  • 资源 - 每一个递归都要保存状态,内存迟早爆掉
  • 有些写的不好的模板类,可能是无限递归 - 要不设限,编译器就hang在那边了

对于Windows下VC9,其默认限制是499(到500就挂了),查看了下编译器参数,没发现有增大最大深度的。

这事也就完了,但有人提出了个用metaprogramming求解sum的好办法(当然不是用N(N+1)/2),蛮有灵感的:

#include <iostream>
using namespace std;

template<unsigned Start, unsigned End>
struct adder{
  static unsigned const Middle = (Start + End) / 2;
  static unsigned const value = adder<Start, Middle>::value + adder<Middle+1, End>::value;
};

template<unsigned End>
struct adder<End, End>{
  static unsigned const value = End;
};

int main()
{
    cout << adder<1, 1000>::value;
}

稍微懂点算法的人,看一下就知道这是用了分治法,每次递归都是两个分叉,这样就把总数度从N降到了LgN,巨大的提高啊!

其实,所谓的编译期运算,对编译器来讲,就是运行期,所以写递归算法也好,改进递归用分治也好,和写普通代码都是一样的道理。

原文地址:https://www.cnblogs.com/baiyanhuang/p/2673462.html