zoukankan      html  css  js  c++  java
  • C++模板元编程

      原理:模板元程序由编译器在编译期解释执行,利用模板特化机制实现编译期条件选择结构,利用递归模板实现编译期循环结构。模板元编程(metaprogramming)意思是,编程系统将会执行我们所写的代码,来生成新的代码,而这些新代码才真正实现了我们所期望的功能。元编程最大的特点在于:某些用户自定义的计算可以在编译期进行,二者通常能够在性能和接口简单性方面带来好处。

      1. 利用模板特化机制实现编译期条件选择结构

      首先了解一下类模板的特化。类模板特化是指将模板参数指定为某一种类型,你必须在起始处声明一个template<>,接下来声明用来特化模板的类型。这个类型被用作模板实参,且必须在类名后面直接指定:

    template<typename T>
    class StackSpecialize
    {
        T m_member;
    };
    
    template<>
    class StackSpecialize<std::string>
    {
        std::string m_member;
    };

      另外一个比较重要的概念是偏特化(Partial Specialization),意思是如果class template拥有一个以上的template参数,可以针对其中某个(或数个,但非全部)template参数进行特化工作。(另一种解释是针对任何template参数更进一步的条件限制所设计出来的一个特化版本)。

      回到模板元编程,如下求斐波那契数列的元编程代码:

    #include "stdafx.h"
    #include <iostream>
    
    using namespace std;
    
    // 主模板
    template<int N>
    struct Fib
    {
        enum {Result = Fib<N-1>::Result + Fib<N-2>::Result};
    };
    // 特化模板必须在主模板之后
    // 完全特化版,处理N=1的情况
    template<>
    struct Fib<1>
    {
        enum {Result = 1};
    };
    
    // 完全特化版,处理N=0的情况
    template<>
    struct Fib<0>
    {
        enum {Result = 0};
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        int i = Fib<500>::Result;
        std::cout<<i<<std::endl;
    
        return 0;
    }

      上述程序编译时,会进行递归实例化,为了计算Fib<500>的enum Result的值,会实例化Fib<499>和Fib<498>,如此递归下去。程序中给出了当N=1和N=0的情况,程序实例化到Fib<1>和Fib<0>时,完全特化版被实例化,递归结束。模板实例化通常要消耗巨大的编译器资源,C++标准建议最多只进行17层递归实例化,上述程序VS下可以正常编译,但VS最多支持500次,501次即报错:Error 1 error C1202: recursive type or function dependency context too complex。

      上述数列在计算时使用的是模板的特化机制实现条件选择结构,还有比如在求最大值的函数中,使用(表达式A?表达式B:表达式C)来实现分支选择,像这种?:符号,编译器在实例化时,不仅实例化表达式B分支,表达式C分支也会实例化。这样造成的一个问题是递归次数的增加,但VS和gcc递归的次数是有限的,所以必须限制实例化数量,使其不至过于庞大。typedef就是一个解决方法,为一个类模板定义一个typedef并不会导致C++编译器实例化该实例的实体。

    // 基础模板:根据第1个实参,来确定是使用第2个实参,还是第3个实参
    template<bool C, typename Ta, typename Tb>
    class IfThenElse;
    
    // 局部特化
    template<typename Ta, typename Tb>
    class IfThenElse<true, Ta, Tb> {
    public:
        typedef Ta ResultT;
    };
    
    // 局部特化
    template<typename Ta, typename Tb>
    class IfThenElse<false, Ta, Tb>
    {
    public:
        typedef Tb ResultT;
    };

      2. 利用递归模板实现编译期循环结构

      以两个向量的点乘为例,比较直观的解决的解决方法当然是维度为循环次数,依次相加相乘。使用元编程编写基本模板后,再编写作为结束条件的局部特化版本,就可以使用元编程实现循环结构了。

      元编程的意义不只是编译期的数值计算,因为元编程的输入的数据必须是已知的,如果程序运行时输入的数据动态变化,那么元编程就无能为力了。问题在于,如果不是纯数值计算,输入的数据都是未知的。元编程还可以用来进行代码生成,编译期断言,类型计算等。Qt就用到代码生成,它将源代码交给标准编译器之前,需要事先将使用 moc (Meta-Object Compiler,“元对象编译器”)分析 C++ 源文件。如果它发现在一个头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。这个新的文件名字将会是原文件名前面加上 moc_ 构成。这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。

      参考资料:http://wenku.baidu.com/view/6b7522d3f111f18582d05ab5.html?from=search

      http://devbean.blog.51cto.com/448512/355100/

      

  • 相关阅读:
    如何为惠普笔记本ProBook 4431S清理电源风扇通风口灰尘
    office app之 Mail App 从新建到发布
    sublime_text3汉化,破解,安装Package_control支持中文显示
    poj3126prime+BFS
    android开发之欢迎界面
    poj 1151 离散化
    DbVisualizer 9 解决中文乱码问题
    RS232、RS422与RS485串口标准简介
    动态创建菜单和动态关联菜单项事件
    更改路径
  • 原文地址:https://www.cnblogs.com/jiayayao/p/6388099.html
Copyright © 2011-2022 走看看