类模板

来源:互联网 发布:淘宝店铺自己做活动 编辑:程序博客网 时间:2024/06/08 02:56

1.编译器不能为类模板推断模板参数类型。需要在通过explicit template argument(显式模板实参)进行实例化

vector<int> 

E45
具有继承关系的模板类AB生成的对象a,b不具有继承关系。为了使模板类实例化的对象指针能够从派生类隐式转换为派生类(例如智能指针不支持隐式转换)

template <typename T> class A{public:    //通过成员初值列表来选择能够进行转换的类型    template <typename U>    A(const A<U>& u1)    :pt(u1.get()) {…}    //泛化构造函数根据对象u创建对象t    T* get() const {return pt;}    //声明泛化copy构造函数/操作符后编译器依然会生成默认的    template <typename U>      A& operator=(A<U> const&);    //声明正常的copy构造函数/操作符来阻止编译器    A(A const&);    A& operator=(A const&);        …private:    T* pt;};

2.定义在类模板外的成员函数

template <typename T>//必须以template开始返回类型 类模板名::函数名(类模板参数列表)//后接类模板参数列表{   …}

3.在类模板的作用域中可以直接使用模板名CP588

template <typename T> class A{public:    A& operator++();//和A&<T> operator++()相等    …};

4.友元可以不是模板

  • 非模板友元可以访问所有模板实例
//声明template <typename> class A;template <typename> class B;template <typename T> class B{   template <typename X> friend class A;   //通过使用与类模板不同的模板参数,友元可以访问所有模板实例   …};
  • 模板友元也可以只授权访问特定模板实例
template <typename T> class B{   friend class A<T>;//模板友元只访问特定模板实例   …};

可以将模板类型参数声明为友元CP590

template <typename T> class A{   friend T;   //对于类型TT将成为A<T>的友元   …};

5.类型别名:引用实例化的类

typedef A<T1> A1;//引用实例化的类template<typename T> using A1=A<T>;//为类模板定义类型别名

6.static数据成员
每个实例类有1个独立的static对象

//声明template <typename T> class A{   static size_t s;   …};//定义template <typename T>size_t A<T>::s=0;

static成员函数只有在使用时进行实例化

auto s=A<T1>::count();

7.成员模板

  1. 不能是虚函数。
  2. 无论是普通类还是类模板可以包含是模板的成员函数。

    普通类的成员模板CP595

//删除器class DbDelete{public:    template <typename T> void operator()(T *p) const;    //之后编译器根据后面的代码推断T的类型    …};//成员模板实例化为DbDelete::operator()<int>(int*)unique_ptr<int,DbDelete>p(new int,DbDelete());

类模板的成员模板CP596

template <typename T> class A{   //构造函数声明  template <typename S> A(S a1,S a2);   …};//类外定义template <typename T>template <typename S>//同时为类模板和成员模板提供模板参数列表        A<T>::A(S a1,S a2):        …

实例化时编译器根据对象的类型推断类模板参数的类型;根据传递给成员模板的函数实参来推断模板实参。

8.类模板特例化CP626
1.需要在原模板定义的命名空间里进行特例化
2.部分特例化:只指定一部分模板参数,本身还是1个模板。
3.只特例化某个成员函数

template <typename T> class A{   void Bar();   …};template <> void Foo<int>::Bar(){   …}

E43 有companyA,companyB,companyC等类,将其作为模板类Msg的模板参数company.

template <typename company> class Msg{public:   void SendClear(const Msg& info)   {       company c;       c.SendCleartext();       …   }   …};

companyC类没有SendCleartext()函数,所以需要针对companyC生成特例化版本(没有SendClear函数)。
模板类Msg有派生类MsgSender,MsgSender的成员函数需要调用基类的SendClear函数。但是编译器发现Msg的特例化版本的接口不一致,所以拒绝在基类寻找该函数。

//方法1void SendClearMsg(const Msg& info)   {       //使用this指针       this->SendCleartext();       …   }//方法2using Msg<company>::SendClear;//在派生类中进行声明

9.traits(1种协议)
要求:对内置类型和用户自定义类型的表现一致
在设计此类型时(以容器的traits为例)

  1. 确定需要保存的类型相关信息(取得容器迭代器的类型)
  2. 如有需要提供特化版本(接收指针类型)
  3. 设计重载函数接收不同类型的信息(doAdvance函数)
  4. 设计控制函数(advance函数)调用相应的重载函数。
//tag struct(卷标结构)负责保存类型相关的信息template <typename iterT>struct iterator_traits{   typedef typename IterT::iterator_category iterator_category;   //将容器迭代器的类型声明成自己的iterator_category   …};

由于指针无法嵌套typedef,所以新定义1个偏特化的版本

template <typename iterT>struct iterator_traits<IterT*>{   typedef random_access_iterator_tag iterator_category;   //由于指针的行为和random_access型的迭代器行为比较相似   …};

以STL的advance函数(将迭代器移动某个给定距离)为例,针对不同类型的迭代器进行不同的操作。

template <typename iterT,typename DistT>//迭代器类型和距离void advance(iterT& iter,DistT d){    if(//迭代器类型为random access)  {…}    else  {…}}

该函数要求用户自定义类型必须有名为iterator_category的typedef,用于确认iterT的类型(卷标结构)。

//在容器的内部typedef random_access_iterator_tag iterator_category;typedef forward_iterator_tag iterator_category;

由于if语句在运行期才会核定。使用重载函数(doAdvance)代替if将此事提前到编译期。

template <typename iterT,typename DistT>void advance(iterT& iter,DistT d){    doAdvance(iter,d,    typename     std::iterator_traits<iterT>::iterator_category();}
template <typename iterT,typename DistT>void doAdvance(iterT& iter,DistT d,               std::random_access_iterator_tag){    …}…
原创粉丝点击