C++ 基础技术再深入(模板)template parameter和template argument(10)---《C++ Templates》

参数化声明

template和class或者function的区别在于templates声明语句有一个参数化子句:
template <…parameters here…>
或者:
export template <…parameters here>
如下展示两种templates:一种在class之内,即member templates,另一种在class之外且namespace scope之内(global scope也被当成一种namespace scope):

template T>
class List{//namespace scope class template
public:
    template 
    List(List const&);//member function template
    ...
};

template T>
template 
List<T>::List(List const& b){
    ...
}
template T>
int Length(List<T> const&);//namespace scope function template

class Collection{
    templateT>
    class Node{//member class template
        ...
    };

    template T>
    class Handle;//member class template,无定义

    template T>
    T* alloc(){//member function template,隐寓为inline函数
        ...
    }
    ...
};

template T>
class Collection::Handle{//member class template
    ...
};

定义域class外的member templates可有多重template <<…>…>参数化子句,其中一个代表template本身,其余各个子句代表外围的每一层class template。这些子句必须从最外层的class templates开始写起。

function template可以有预设的调用自变量,和一般function一样:

template <typename T>
void report_top(Stack const&,int number=10);

template <typename T>
void fill(Array*,T const&=T());//若T为内建类型,T()为0或者false

当fill()被调用时,如果调用者提供了第二自变量值,预设自变量便不会被实例化,这可确保如果预设自变量无法别某个特定类型T实例化的时候,不会引发编译错误。举例如下:

class value{
public:
    Value(int);
};
void init(Array* array){
    Value zero(0);
    fill(array,zero);//OK
    fill(array);//ERROR:Value没有default构造函数,所以调用失败
}

除了两种template基本类型,另有三种声明也可以被参数化,三者均相当于class template的成员定义:
1)class templates的成员函数定义;
2)class templates的nested class members(嵌套类别成员)定义;
3)class templates的static成员变量定义。
虽然它们也可以被参数化,但是它们并不是第一级templates。它们的参数完全由它们所隶属的template决定。示例如下:

template 
class CupBoard{
    void open();
    class Shelf;
    static double total_weight;
    ...
}

template 
void CupBoard<T>::open(){
    ...
}
template 
class CupBoard::Shelf{
    ...
};
template 
double CupBoard::total_weight=0.0;
  • 虚拟成员函数

member function templates不能被声明为virtual,这个限制的原因在于:虚拟函数调用机制使用一个大小固定的表格,其中每一笔条目记录一个虚拟函数入口,然而直到整个程序编译完成后才能知道有多少个member function templates需要被实例化,因此和虚拟函数调用机制冲突。
但是class template members却可以是virtual函数,因为class被实例化时候,member function的数量早就确定了,因此可以为虚拟函数。

template 
class Dynamic{
public:
    //class template的member function,可以被声明为virtual
    virtual ~Dynamic();
    //member function template,不可以被声明为virtual
    template 
    virtual void copy(T2 const&);
};
  • template的命名机制

每个template在其作用域内必须有一个独一无二的名称,除非是被重载的function templates。需要特别注意的是class template不能喝其他不同种类的物体共享同一个名称,这点与一般的non-template class不同。

int C;
class C;//class名称和nonclass名称处在不同的空间内

int X;
template <typename T>
class X;//ERROR:名称与上述变量X冲突

struct S;
template <typename T>
class S;//ERROR:名称与上述struct S冲突

template通常使用外部链接,但不能使用C链接方式,惟一例外是static namespace scope function templates,函数内部不能再声明template,默认为:

extern "C++" tempalte <typename T>
void normal();

还有一种非标准形式的链接

extern "Xroma" template <typename T>
void Xroma_link();
tempalte 
void external();//直射另一个文件中同名且作用域相同的物体

tempalte 
static void internal();//与另一个文件中的同名template无关
  • Primary Template(主模板/原始模板)

主模板的声明语句在template名称之后并不添加由角括号括起来的template argument list

template <typename T> class Box;//OK:primary template
template <typename T> class Box;//error:non-primary template
template <typename T> void translate(T*);//ok:primary template
template  void translate(T*);//error:non-primary template

一旦我们声明一个偏特化的template,就产生了一个non-primary template。

Template Parameter(模板参数)

template parameters有三种类型:
1)Type parameters(类型参数):
2)Nontype parameters(非类型参数):
3)template template parameters(双重模板参数)。
template parameters是在template声明语句的参数化子句中生命的变量,template parameters不一定得具名:

template <typename,int>
class X;

但是当template程序代码中需要用到某个template parameter时,后者必须具名,注意:后面声明的template parameters可以用到前面声明的template parameters的名称:

template <typename T,T* Root,template  class Buf>
class Structure;

下面我们各个击破。

  • Type Parameters(类别参数)

类别参数由关键字typename或者class导入,两者完全等价,声明方式是:关键词typename或class后面跟一个简单的标识符,该符号后面可以跟一个逗号以便区隔下一参数,也可以使用一个右角括号结束子句,或者跟一个等号表示预设模板自变量。
template声明语句中type parameter的作用非常类似typedef的名称。例如,你不能使用class T这样的名称,及时T确实表示一个class。

template 
class List{
    class Allocator* allocator;//ERROR
    friend class Allocator;//ERROR
    ...
};
  • Nontype Parameters(非类型参数)

非类型参数实质可以在编译期或者链接期就可以确定其值的常数。这种参数的类型必须是如下三者之一:
整数(int)或者enum类型;
pointers:指向常规objects、执行functions和指向members;
reference:指向objects和指向functions。
你可能惊喜的发现,nontype parameter前面也可以有typename,示例如下:

template <typename T,typename T::Allocator* Allocator>
class List;

Nontype parameters也可以是function类型或者array类型,但它们都会退化为对应的pointer类型:

template<int buf[5]>
class Lexer;
tempalte <int* buf>
class Lexer;

Nontype template parameters的声明防护四非常类似于变量声明,但你不能加上诸如static、mutable之类的修饰,但是却可以加上const或者volatile,但如果这些修饰词出现在参数类型的最外层,编译器会忽略它们:

template <int const length> //const被忽略
class Buffer;
template <int length>
class Buffer;//与上一行声明语句等价

最后,强调一下,non type parameters总是右值:不能被取址,也不能被赋值。

Template Template Parameters(双重模板参数)
Template Template Parameter是一种class template占位符号,其声明方式和class templates类似,只是不能够使用关键词struct和union。

template <template<typename X> class C>//OK
void f(C<int>* p);

template <template<typename X> struct C>//ERROR:不能使用关键词struct
void f(C<int>* p);

template <template<typename X> union C>//ERROR:不能使用关键词 union
void f(C<int>* p);

在它们的作用域内,你可以像使用class template那样地使用template template parameters。
Template template paramters的参数也可以有default template arguments(预设模板自变量)。如果客户端没有为相应的参数指定自变量,编译器就会使用这些预设自变量:

template