类模板

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的特例化版本的接口不一致,所以拒绝在基类寻找该函数。

//方法1
void SendClearMsg(const Msg& info)
   {
       //使用this指针
       this->SendCleartext();
       …
   }
//方法2
using 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)
{
    …
}

…
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值