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:
※编译器遇到模板定义时不生成代码,仅当我们实例化时才生成。
※调用者要保证传递给模板的实参支持模板所要求的操作。
头文件:
- 调用函数时,编译器只需要有函数的声明即可。
- 使用类时,编译器必须确定类定义可用,成员函数声明即可。
- 模板不同,为了实例化,编译器需要掌握函数模板或类模板成员函数的定义,所以模板头文件包括声明与定义。
模板编译错误:
①编译模板本身,语法错误。
②编译器遇到模板使用,检测用户提供的模板实参是否相匹配(个数,类型)。
③实例化,发现类型相关错误,是否可用相应的操作符等。