• 《c++ templates》学习笔记(12)——第十七章 metaprogram


     

    1       第十七章 metaprogram

    1.1    Metaprogram的第一个例子

    书上举了一个计算3N次幂的例子,我将其扩展为计算MN次幂。代码如下:

    template<int M, int N>

    class Power{

    public:

        enum{ result = M*Power<M, N-1>::result};

    };

     

    template<int M>

    class Power<M, 0>{

    public:

        enum { result = 1};

    };

    事实上,Template metaprograming后面所做的工作就是递归的模板实例化。

    此处的Power就被称为一个template metaprograming。它描述了一个可以在翻译期求值的计算。

    1.2    枚举值和静态常量

    在老版的c++中(比如vc6),在类声明的内部,枚举值时声明“真常值”的唯一方法。然而,现在的情况发生了变化,c++标准化过程中引入了在类内部进行静态常量初始化的概念(在vs2003之后即可)。

    所以,上面的枚举值其实可以用静态常量来代替。代码如下:

    template<int M, int N>

    class Power{

    public:

        //enum{ result = M*Power<M, N-1>::result};

        static int const result = M*Power<M, N-1>::result;

    };

     

    template<int M>

    class Power<M, 0>{

    public:

        //enum { result = 1};

        static int const result = 1;

    };

    得到的结果是一样的。

    然而,该版本存在一个缺点:静态成员变量只能是左值。因此,如果你有一个如下声明:

    Void foo(int const&);

    而且你把metaprogram的结果传进去,即:

    Foo(Power<3,4>::result>;

    那么编译器必须传递Power<3,4>::result的地址。这会强制编译器实例化静态成员的定义,并且为该定义分配内存。

    但是枚举值却不是左值(即没有地址)。因此在你使用引用传递时,并不会使用任何静态内存,就像是以文字常量的形式传递这个完成计算的值一样。所以,我们一般鼓励使用枚举值

    1.3    第二个例子:计算平方根

    template<int N, int LO=0, int HI=N>

    class Sqrt{

    public:

        enum{ mid = (LO+HI+1)/2};

        enum{ result = (N<mid*mid) ? Sqrt<N,LO,mid-1>::result: Sqrt<N, mid, HI>::result};

    };

     

    template<int N, int M>

    class Sqrt<N, M, M>{

    public:

        enum{result = M};

    };

    int _tmain(int argc, _TCHAR* argv[])

    {

        //17.3

        i = Sqrt<4>::result;

        return 0;

    }

    在上面的例子中,如果我们将其展开,那么会得到:

        Sqrt<4>::result;

        //mid = (0+4+1)/2 = 2

        =(4<2*2)? Sqrt<4,0,1>::result : Sqrt<4, 2, 4>::result;

            //其中Sqrt<4,0,1>::result

            //mid = (0+1+1)/2=1

            =(4<1*1) ? Sqrt<4,0,0>::result : Sqrt<4, 1, 1>::result;

            =(4<1*1) ? 0:1;

            =1;

            //其中Sqrt<4, 2, 4>::result

            //mid=(2+4+1)/2=3

            =(4<3*3) ? Sqrt<4, 2, 2>::result : Sqrt<4, 3, 4>::result;

            =(4<9) ? 2 : Sqrt<4, 3, 4>::result;    

                //其中Sqrt<4, 3, 4>::result

                //mid = (3+4+1)/2=4

                =(4<4*4)? Sqrt<4, 3, 3>::result : Sqrt<4, 4, 4>::result;

                =(4<16)? 3 : 4;

                =3;

            =2

        =2

    我们看到,上面得到了以下的这些实例化体:

    Sqrt<4, 0, 0>::result

    Sqrt<4, 1, 1>::result

    Sqrt<4, 2, 2>::result

    Sqrt<4, 3, 3>::result

    Sqrt<4, 4, 4>::result

    Sqrt<4, 0, 1>::result

    Sqrt<4, 2, 4>::result

    Sqrt<4, 3, 4>::result

    由于代码中试图使用::运算符来访问类的成员,所以类中的所有成员同时会被实例化。这就意味着:不仅实例化条件运算符正面分支的模板,还会实例化负面分支的模板。这将会导致庞大的实例化体。

    对于任何编译器来说,模板实例化通常都会是一个代价高昂的过程。

  • 相关阅读:
    第七章 查询数据(待续)
    第六章 MySQL函数(待续)
    第五章 数据类型和运算符(待续)
    Centos 用户登录失败N次后锁定用户禁止登陆
    关于CentOS普通用户无法登录SSH问题
    允许FTP用户登录并禁止Shell登录的方法
    禁止Linux用户登录方法
    syslog-ng 学习心得与配置说明
    记录linux系统用户shell终端操作记录
    通过syslog接收远程日志
  • 原文地址:https://www.cnblogs.com/strinkbug/p/1347056.html
Copyright © 2020-2023  润新知