zoukankan      html  css  js  c++  java
  • 模板实例化

    如果说上一篇博文《模板名称》是教人怎么写模板,那么这一篇就是教人怎么使用模板。

    阅读此文之前要先阅读上一篇博文理解什么事受限名称,什么是依赖型名称 → 

    http://www.cnblogs.com/claruarius/p/4063795.html

    模板实例化的复杂性在于:对于产生自模板的实体,它们的定义已经不再局限于源代码中的单一位置。
    一、理解两个概念
    (1)实例化:实例化在C++中通常指“根据类型创建一个对象”,但是在模板里面,实例化是指使用具体值替换模板实参,从模板中产生普通类,函数或者成员函数过程
    (2)特化:这个过程最后获得的试题就是我们所说的特化。
    然而,实例化过程并不是特化的唯一方式,还有显式特化,通过引入一个template<>来实现,如下:
    template<typename T1, typename T2>
    class MyClass{};
     
    template<>
    class MyClass<std::string, float>{};
     
    二、按需实例化(on-demand实例化)
    如果(某个组件)期望知道模板特化的大小,或者访问该特化的成员,那么整个定义就需要位于作用域中。
    比如显示的调用模板的成员,或者是包含隐式转换
    (1)显示调用成员函数
    template<typename T> class C;   //前置声明
    C<int>* p = 0; //这里只需要声明就够了
     
    template<typename T>
    class C{
        public:
            void f();
    };
     
    void g(C<int>& c){
        c.f();                    //此处需要知道整个模板的定义,因为编译器要确定f()是不是可以被访问到
    }
     
    (2)隐式类型转换
    C++重载规则要求:如果候选函数的参数是class类型,那么该类型所对应的类就必须可见
    template <typename T>
    class C{
    public:
        C(int);  //单参数隐式类型转换
    };
     
    void candidate(C<double> const&); //①允许编译器实例化该重载函数,但不是必须的,在VS2013中,就没有实例化参数
    void candidate(int){}                        //②
     
    int main()
    {
        candidate(42);         //编译器不会选择①处的声明,因为一个精确的匹配要优于显式转型所获得的匹配
        return 0;
     
    三、延迟实例化
    编译器只对确实需要的部分实例化。换句话说,编译器会延迟模板的实例化。
    (1)当隐式实例化类模板时,同时也实例化了该模板的每一个成员函数的声明,但并没有实例化相应的定义。
    但是有些情况是不会延迟的,如下:
    ①类模板里面包含有匿名的union,那么,匿名的union成员同时也被实例化,
    ②虚函数,作为实例化类模板的结果,许多编译器实现都会实例化虚函数的定义,因为“实现虚函数调用机制的内部结构”要求虚函数的定义作为链接实体的存在。
    (2)实例化类模板与实例化缺省的函数调用实参是分开的。换句话说,只有函数确实使用了缺省的实参,才会实例化该实参,如果这个函数不使用缺省的实参,那么就不会实例化该缺省的实参,而是显式使用实参来实例化。
    template<typename T>
    class Safe{};
     
    template<int N>
    class Danger{
        typedef char Block[N];            //如果N<=0的话,将会出错
    };
     
    template<typename T, int N>
    class Tricky{
    public:

        virtual ~Tricky(){}    //虚函数,并提供了定义
        void no_body_here(Safe<T>=3);     //该缺省实参是可疑的,但没有被使用,不会被实例化,不会出错
        void inclass(){
            Danger<N>no_boom_yet;          //没有被使用,不会被实例化,不会出错
    }
    //void error(){ Danger<0> boom;}      //如果没有被注释,会被要求给出这个类Danger<0>的完整定义,
    //而实例化Danger<0>会出错,即使没有被使用,也不会被实例化,但仍然能够引发一个错误
    //该错误是在泛模板处理中产生的
    //void unsafe(T(*p)[N]);        //如果 没有注释掉的话,此处实例化声明的时候会出错
    T operator->();
    //virtual Safe<T> suspect();     //虚函数,但是没有提供定义,所以会引发一个链接期的错误,
    //如果不注释掉的话,链接器就会给出这类错误
    struct Nested{
        Danger<N> pfew;      //因为没有使用该结构,所以此处的没有实例化

    };
     

    union{

     

        int align;

     

    Safe<T> anonymous;

     
    };
    };
     
    int main()
    {
        Tricky<int, 0> ok;
    }
    三、C++实例化模型 
    (1)两阶段查找
    第一阶段:使用普通查找规则(在适当情况也会使用ADL)对模板进行解析,查找非依赖型名称。另外非受限的依赖型名称(诸如函数调用中的函数名称,因为其具有一个依赖型实参)也会在这个阶段查找,只不过查找不完全,在实例化模板的时候还会再次进行查找。
     
    第二阶段:发生在模板被实例化的时候,我们也称此时发生的地点(或源代码的某个位置)为一个实例化点POI。依赖型受限名称就在此时查找。另外,非受限的依赖型名称在此阶段也会再次执行ADL查找.
     
    (2)POI(实例化点)
    ①对于指向非类型(也就是函数╮( ̄▽ ̄")╭)特化的引用, C++把他的POI定义在“包含这个引用定义或声明之后的最近名字空间域”。
    class MyInt{
        public:
        MyInt(int i);
    };
     
    MyInt operator - (MyInt const);
    bool operator >(MyInt const&, MyInt const&);
    typedef MyInt int;    //②
    void e(){}//③
    template <typename T>
    void f(T i)   
    {
        if(i>0){
            g(-i);   //①
            e(); //非受限非依赖型名称会在第一阶段查找,所以如果要顺利解析需要在此模板之前(可以在③位置)定义函数e(),否则解析不通过
            h(-i);//h是非受限依赖型名称,所以函数h不必出现在模板f()之前,让其可见(因为可以通过ADL查找到名称),如下。
    }
    }
    //(1)
    void g(Int)    //这就是那个定义或声明
    {
    //(2)
        f<Int>(42); //这就是那个引用
    //(3)
    }
    //(4)这就是那个“之后最近的名字空间域”,函数f<int>的一个特化会出现在这里
     
    void h(Int)
    {
        f<Int>(32);
    }
     
    【注意】①位置的名称g, 是非受限依赖型名称,因为他的参数是依赖型的, 所以会在第二阶段查找只是使用ADL就能够找到函数g(Int)其实也就是g(MyInt);如果将MyInt替换成int,即②处为typedef int Int,那么第二阶段的查找关联命名空间就会是空集,也就找不到函数g(Int)的声明和定义。
     
    ②对于产生自模板的类实例的引用,它的POI只能定义在“包含这个实例引用定义或声明之前的最近名字空间域”。
    template<typename T>
    class S{
         public:
            T m;
    };
    //(5) 这里就是S的POI
    unsigned long h() //这就是那个定义或声明
    {
        //(6)
        return (unsigned long) sizeof(S<int>);  //这就是那个实例引用
        //(7)
    }
    //(8) 
     
    四、显式实例化  
    为模板特化显式的生成POI是可行的,我们把这种特化的构造成为显式实例化指示符。从语法关键字上讲,它有关键字template和后面的特化声明组成,所声明的特化就是即将有实例化获得的特化。
     
    template<typename T>
    void f(T) throw(T){}
     
    下面有4个有效的显式实例化实体
    template void f<int>(int) throw(int);
    template void f<>(int) throw(int); 
    template void f(int) throw(int); //通过演绎获得
    template void f(int); //异常规范也可以省略,如果没有省略,异常规范必须匹配相应的模板
     
    C++规定: 同一个程序中,每一个特定的模板特化最多只能存在一处显式实例化。而且,如果摸个模板特化已经被显式实例化(使用template),那么就不能对其进行显式特殊化(使用template<>)。
     
  • 相关阅读:
    apue学习笔记(第十二章 线程控制)
    apue学习笔记(第十一章 线程)
    apue学习笔记(第十章 信号)
    apue学习笔记(第九章 进程关系)
    apue学习笔记(第八章 进程控制)
    apue学习笔记(第七章 进程环境)
    apue学习笔记(第六章 系统数据文件和信息)
    apue学习笔记(第五章 标准I/O)
    apue学习笔记(第四章 文件和目录)
    apue学习笔记(第三章 文件I/O)
  • 原文地址:https://www.cnblogs.com/claruarius/p/4067868.html
Copyright © 2011-2022 走看看