模板与泛型编程1

时间:2021-09-18 04:20:02

1.函数模板

1.1 样例

template<typename T,class U>           //可用typename或class
inline T fcn(T v1,U v2){/*...*/}       //inline可选

1.2 非类型模板参数

template <typename T,siez_t N>
size_t arr_size(const T(&p)[N])        //返回数组大小
{
    return N;
}

2.类模板

2.1 样例

template <typename T>              //类模板
class A
{
    A fcn(...);
};
template <typename T>             //成员函数定义
A<T> A<T>::fcn(...)               //不在模板作用域中,需要指定模板实参
{
    A...                         
}

不能为类模板推断参数类型,需以< … >提供额外信息。

2.2 类模板与友元

template <typename T>
class B
{
    friend class A<T>;                      //与B有相同实例类型的A为友元
    template <typename X> friend class C;   //C的所有实例与B的每个实例为友元
    friend class D;                         //与普通类D为友元
    friend T;                               //C++11可以将模板类型参数声明为友元
};

2.3 模板类型别名

typedef A<string> strA;        //√
typedef A<T> TA;               //×
/*c++11 using */
template<typename T>
using twing = pair<T,T>;      //使得twin<string> = pair<string,string>

2.4 类模板static成员

template <typename T>
size_t A<T>::s = 0;          //类外初始化

对任意给定类型x,都有一个A< x >::v,对所有A< x >类型的对象共享相同的v对象。

2.5 模板参数与作用域

typedef double A;
template <typename A,typename B>
void f(A a,B b)
{
    A temp = a;                   //①此处A不是表示double
    double B;                     //②重声明模板参数B,出错
}
template <typename V,typename V>  //③模板参数重复
  • 模板参数会隐藏外层作用域中声明的相同名字①
  • 模板内不能重用模板参数名②
  • 参数名不能重用,所以模板参数名不能重复③

2.6 使用类的类型成员

T::size_type *p;             //默认size_type为变量,则执行*运算
typename T::size_type *p;    //使用typaname显示指定size_type为类型

2.7 模板默认实参

template <typename T=int>
class Numbers
{
    /*...*/
};
Numbers<> v;        //v为Numbers<int>类型

2.8 控制实例化

原:
  模板被使用时才会实例化,则相同的实例可能出现在多个对象文件中,产生很多不必要的实例。
解:
  C++11可通过显示实例化来避免这种开销。

extern template class A<string>;      //A<string>的声明
       template class A<string>;      /*A<string>的定义,与处理类模板的普通实例化不同,编译器会实例化该类的所有成员*/

extern表示承诺在程序其它位置有该变量,类,函数的一个定义。

2.9效率与灵活性

灵活:
shared_ptr(运行时绑定删除器,使用户重载删除器更方便):创建reset指针时传递给它一个可调用对象即可。

shared_ptr<T> sp(p1,d1);     //创建时指定
sp.reset(p2,d2);             //reset一个新指针并指定删除器

效率:
unique_ptr(编译时绑定删除器):定义unique_ptr时以显示模板实参的形式提供删除器的类型,避免间接调用的开销,性能高。

unique_ptr<T,D> u(d);        //D为删除器类型

3.模板编译

Tips:
  编译器遇到模板定义时不生成代码,仅当我们实例化时才生成。
  调用者要保证传递给模板的实参支持模板所要求的操作。
头文件:

  • 调用函数时,编译器只需要有函数的声明即可。
  • 使用时,编译器必须确定类定义可用,成员函数声明即可。
  • 模板不同,为了实例化,编译器需要掌握函数模板或类模板成员函数的定义,所以模板头文件包括声明与定义

模板编译错误:
  ①编译模板本身,语法错误。
  ②编译器遇到模板使用,检测用户提供的模板实参是否相匹配(个数,类型)。
  ③实例化,发现类型相关错误,是否可用相应的操作符等。