《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原则允许试图创建无效的类型,但是并不允许试图计算无效的表达式。

原文地址:https://www.cnblogs.com/strinkbug/p/1339607.html