函数模板
模板定义以关键字template开始,后接模板参数列表,编译器用推断出来的模板参数进行实例化一个特定版本的函数
类型前加typename或class,表明是类型模板参数
非类型模板参数
用特定的类型名来定义非类型模板参数,实例化时编译器用常量表达式的值进行代替(例如unsigned)
声明inline和constexpr
放在模板参数列表之后,返回类型之前
模板编译
模板在实例化后才生成代码,实例化时编译器需要模板的全部定义,所以一般模板的声明和定义都放在头文件
类模板
编译器不能为类模板推测函数类型,必须提供额外信息,当实例化类模板时,必须在类名后面注明元素类型
类模板的成员函数可以在模板内外定义,但是由于模板的每个实例化都是不同的成员函数,所以成员函数具有和模板相同的模板参数,因此在类外定义模板类的成员函数时,要用关键字template开始,后接带模板参数列表的类作用域,最后是成员函数体。类模板成员函数只有在使用到的时候才会实例化
在模板类作用域内,可以只使用类名表明模板类类型而不需要模板实参列表,编译器默认与模板类的实参列表一致
类模板和友元
友元模板,即使用模板参数建立友元,可以实现访问特定实例的类
template <typename T> class pal; class C { friend class Pal<C>;//使用C实例化的Pal是友元 template<typename T> friend class Pal2;//Pal2的所有实例都是友元 }; template <typename T> class C2 { friend class Pal<T>;//C2的每个实例将相同实例化的Pal声明为友元 template<typename X> friend class Pal2;//Pal2的所有实例都是C2的友元 friend class Pal3;/非模板的友元 };
在新标准中,可以将模板类型参数声明为友元
类模板的static成员
模板类的每个实例都有一个独有的static对象,但是他们共享相同的static成员数据
static成员只有在使用时才会实例化
模板参数与作用域
一个模板参数名的可用范围是在其声明之后到模板声明或定义结束之前,与其他名字一样模板参数名字会隐藏外部作用域的名字
在模板内不能重用模板参数名
声明中模板参数的名字不必与定义中相同,但是数量和种类要一致
使用类的类型成员
在类型名字前加typename表明这个名字是类型名,从而使用类作用域运算符访问类的成员
默认模板实参
template <typename T,typename F = less<T>> int compare(const T &v1, const T &v2, F f = F()) { if(f(v1, v2)) return -1; if(f(v2, v1)) return 1; return 0; }
在实例化时,使用空的尖括号表示使用默认实参,跟函数默认实参一样,默认实参只能从右向左替换
成员模板
成员模板不能是虚函数
控制实例化
相同的实例可能出现在多个文件中,为了避免资源浪费,可以使用显式实例化
extern template declaration; template declaration;
可以多次使用extern进行声明,但是使用template的定义只能有一个,从而只有在遇到定义时才会实例化
使用这种方法进行实例化,会直接实例化所有成员
效率与灵活性
shared_ptr不会保存删除器,采用简介保存的方法,而unique_ptr是直接保存删除器的