zoukankan      html  css  js  c++  java
  • 模板元编程的递归限制

    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,巨大的提高啊!

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

  • 相关阅读:
    BUAA OO Unit1 表达式求导
    中介者模式
    命令模式
    观察者模式
    解释器模式
    策略模式
    迭代器模式
    模板方法模式
    代理模式
    桥接模式
  • 原文地址:https://www.cnblogs.com/baiyanhuang/p/2673462.html
Copyright © 2011-2022 走看看