• 《c++ templates》学习笔记(7)——第八章 深入模板基础(1)


    1       第八章 深入模板基础

    1.1    参数化声明

    1.1.1   联合模板

    联合模板也是允许的,比如:

    template<typename T>

    union AllocChunk{

         T object;

         unsigned char bytes[sizeof(T)];

    };

     

    1.1.2   函数模板的缺省调用实参

    template< typename T>

    void fill(Array<T>*, T const& = T());

    该例子说明,缺省调用实参可以依赖于模板参数。

    在调用fill时,如果提供了第二个函数调用参数的话,就不会实例化这个缺省实参。这同时也说明了:即使不能基于特定类型T来实例化缺省调用实参,也可能不会出现错误。比如:

    class Value{

    public:

         Value(int){};

    };

     

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

    {

         std::list<Value>* pList = new std::list<Value>();

         Value aValue(1);

         //fill(pList);         //错误

         fill(pList, aValue); //正确

         return 0;

    }

     

    除了类模板和函数模板之外,我们还应当注意:

    (1)       类模板的成员函数的定义;

    (2)       类模板的嵌套类的成员的定义;

    (3)       类模板的静态数据成员的定义;

     

    1.1.3   虚成员函数

    成员函数模板不能被声明为虚函数。

    注意,类模板的成员函数模板,而不是类模板的成员函数。

     

    template<typename T1>

    class Dynamic{

    public:

         virtual ~Dynamic();//OK,每个Dynamic都有一个析构函数

         template<typename T2>

         virtual void copy(T2 const&);//ERROR,在确定Dynamic<T>实例的时候不知道copy()的个数

    };

     

    1.1.4   模板的链接

    类模板不能和另一个实体共享一个名称。例如:

    int C;

    template<typename T>

    class C;//错误,名称冲突

    书上是这么说的,但是经过我在vs2005下验证,以下代码是不会报错的:

    int C;

    template<typename T>

    class C

    {

    public:

         C(){};

         void Handle(){

             std::cout<<"has error?"<<std::endl;

         };

    };

    但是如果我试图对类C做如下实例化,则会报错:type 'int' unexpected

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

    {

         C<int> aC;

         aC.Handle();

         return 0;

    }

    我怀疑就是由于在实例化的时候,他把C当作那个变量了。

     

    1.1.5   基本模板

    如果模板声明的是一个普通声明,一就是模板名称后面没有一对尖括号,我们就称其为基本模板。

     

    函数模板必须是基本模板,类模板的特化则不是基本模板。

     

    1.2    模板参数

    分为三类:

    l         类型参数;

    l         非类型参数;

    l         模板的模板参数;

    位于后面的模板参数声明可以引用前面的模板参数名称。

    template<typename T, T ROOT, template<T> class Buf>

    class Structure;

    此处,第二个和第三个参数都引用了第一个参数。

    第二个参数是非类型模板参数;

    第三个参数是一个模板的模板参数,这个Buf是一个带有非类型模板参数的模板。

     

    其实上面的这样的定义在vs2005中是有问题的,比如:

    template<int NUM>

    class Structure1{

     

    };

     

    template<typename T, T ROOT, template<T> class Buf>

    class Structure{

    public:

         Structure(){

             int i = ROOT;

         };

     

    private:

         T firstMem;  

         Buf<ROOT> thirdMem;

    };

     

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

    {   

         Structure<int, 10, Structure1> aStr;

         return 0;

    }

    上面的这么一段代码,在vs2005中编译,则会报如下错误:

    error C3201: the template parameter list for class template 'Structure1' does not match the template parameter list for template parameter 'Buf'

     

    1.2.1   类型参数

    书上说一下的形式会出错:

    template<typename T>

    class List{

         class T* alloctor;

         friend class T;

    };

    但是我在vs2005中,其实编译上面的代码并不会出错,但是编译下面这段代码则会报错:

    template<typename T>

    class List{

         friend class T;

    private:

         void Handle(){

             std::cout<<"has error"<<std::endl;

         };

    };

     

    class C

    {

    public:

         C(){

            

         };

    public: 

         void SetList(List<C>* p){       

             p->Handle();

         };

    };

     

     

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

    {   

         List<C> aList;

         C aC;

         aC.SetList(&aList);

         return 0;

    }

    错误信息为:List<T>::Handle' : cannot access private member declared in class 'List<T>'

     

    这说明vs2005内还是没有实现这种类型的友元。

     

    1.2.2   非类型参数

    非类型参数的表示是:在编译期或者链接期可以确定的常值

    非类型模板参数的声明和变量的声明很相似,但是不允许有staticmutable等修饰符。

    非类型模板参数必须是以下的一种:

    l         整形或者枚举类型

    l         指针类型(可以是普通对象的指针类型、函数指针类型、指向成员的指针类型);

    l         引用类型(指向对象或者指向函数的引用)。

     

    1.2.3   模板的模板参数

    模板的模板参数是代表类模板的占位符。

    模板的模板参数可以具有缺省模板实参。

    对于模板的模板参数而言,他的参数名称只能被其自身其他参数的声明使用,所以下面的声明是错误的。

    template< template<typename T> class List>

    class Node{

              T* storage;

    };

    模板的模板参数的参数的名称(如上面的T)并不会在后面用到,因此,该参数也经常可以被省略不写,即没有命名。

     

    1.2.4   缺省模板实参

    只有类模板才有缺省模板实参。

    只有在后面的模板参数都提供了缺省实参的前提下,才能具有缺省模板实参。后面的缺省值通常是在同一个模板参数中提供的,但是也可以在前面的模板声明中提供。例如:

    typename< typename T1, typename T2, typename T3, typename T4=char>

    class Quintuple;

     

    typename< typename T1, typename T2, typename T3=int, typename T4>

    class Quintuple;//正确

     

    typename< typename T1=std::string, typename T2, typename T3, typename T4>

    class Quintuple;//错误

    另外,缺省实参不能重复声明,所以下面的第二个声明是错误的,但是根据我在vs2005上的实验,只会发出警告,可能是警告级别设置的问题。

    template<typename T=void>

    class Value;

    template<typename T=void>

    class Value;

     

    1.3    模板实参

    可以有几种方式来确定模板实参。

    l         显示模板实参;

    l         注入式类名称,对于具有模板实参P1, P2…的类模板X,在他的作用域内,模板名称X等同于其template-id,即:X<P1, P2,…>

    l         缺省模板实参。这个只对类类模板有效,然而即使所有的模板参数缺省实参,一对尖括号也是不能省的。

    l         实参演绎,这个只对函数模板有效,如果能够演绎,则后面的尖括号可不要。

     

    1.3.1   函数模板实参

    函数模板实参一般都可以通过演绎得到。

    但是有一些模板实参永远得不到演绎的机会,于是,我们最好把这些实参所对应的参数模板参数列表的开始,从而可以显示指定这些参数,从而其他的参数仍然可以得到演绎。

    template<typename destT, typename srcT>

    inline destT implicit_cast(srcT const& x)

    {

         return (destT)x;

    }

     

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

    {   

         int i = implicit_cast<int>(1.0);

         return 0;

    }

    我怀疑c++中的几个转型函数都是这么定义的。

     

    到这里,我们要来看一个书中给的叫简单的元编程的例子:

    typedef char RT1;

    typedef struct{ char a[2]; } RT2;

     

    template<typename T> RT1 test(typename T::X const*);

    template<typename T> RT2 test(...);

     

    #define type_has_member_type_X(T) (sizeof(test<T>(0)) == 1)

     

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

    {   

         if(type_has_member_type_X(int))

             std::cout<<"int has member type X"<<std::endl;

         else

             std::cout<<"int does not have member type X"<<std::endl;

         return 0;

    }

     

    这个type_has_member_type_X 宏就可以用来确定指定的类型是否有X子类型。

    这里正事SFINAE,即“替换失败并非错误(substitution failure is not an error)”原则。

     

    SFINAE原则允许试图创建无效的类型,但是并不允许试图计算无效的表达式。

  • 相关阅读:
    WebGoat之Injection Flaws
    WebGoat之Web Services
    XPath语法
    用Maven在Eclipse中配置Selenium WebDriver——方法1
    用Maven在Eclipse中配置Selenium WebDriver——方法2
    Enum与enum名字相互转换
    数据库数据类型varchar(n)与nvarchar(n)比较
    网页切图工具
    Html标签
    在VS的Solution Explorer中高亮当前编辑的文件
  • 原文地址:https://www.cnblogs.com/strinkbug/p/1339607.html
Copyright © 2020-2023  润新知