有时我们需要对许多不同类型的数据进行相同的操作,如排序比较大小等。相比于为每一种数据类型的函数都定义一种函数执行对应的操作,编写一个模板函数来得更为简便有效。
1、模板定义以关键字template开始,后跟一个模板参数列表(等价于函数形参列表)。
template<typename T>//定义一个包含一个模板参数T的模板
2、调用一个模板时,编译器用函数实参来推断模板实参。(编译器用推断出的模板参数为我们实例化一个特定版本的函数)
3、编译器生成的版本通常被称为一个实例。
4、模板类型参数可以看作类型说明符,像类型说明符、类说明符一样使用。
template <typename T> T foo(t* p)
{
T temp = *p; //temp类型将是指针p指向的类型
//......
return temp;
}
5、非类型的模板参数表示一个值而非一个类型。
template<unsigned N, unsignded M>
int compare(const char (&p1)[N],const char (&p2) [M])
{
return strcmp(p1,p2);
}
6、一个非类型参数可以是一个整数,或者一个指向对象或者函数类型的指针或(左值)引用。绑定到非类型整数参数的实参必须是一个常量表达式。绑定到指针或者引用的非类型参数必须具有静态的生命周期。
7、在模板的定义中,模板的非类型参数是一个常量值。
8、编写泛型代码时,函数的参数设定为Const的引用,保证了函数可以用于不能拷贝的类型。
9、为了生成一个实例化的模板,编译器需要掌握函数模板或类模板成员函数的定义。与非模板代码不同,模板的头文件必须包含声明和定义。
10、函数模板实例示例:
实现类似标准库find算法的模板。函数接受两个模板一个接受迭代器参数、另外一个表示值得类型。
#include <iostream>
#include <string>
#include <vector>
#include <list>
using namespace std;
template <typename I,typename T>
I find (I b ,I e,const T & v)
{
while(b!=e&& *b!=v)
{
b++;
}
return b;
}
int main ()
{
vector <int> vi = {0,2,4,6,8,10};
list<string> ls ={"Hello","World","!"};
auto iter = find(vi.begin,vi.end,6);
if(iter ==vi.end)
cout << "can not find 6" << endl;
else
cout << "find 6 at position" << iter-vi.begin() << endl;
return 0;
}
11、类模板是生成类的蓝图,编译器不能为类模板推断模板的参数类型。
template <tyepname T> class Blob
{
public:
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
//构造函数
Blob();
Blob(std::initializer_list<T>il);
//Blob中的元素数目
size_type size() const {return data->size();}
bool empty() const {return data->empty();}
void push_back(const T && t){data->push_back(std::move(t));}
void pop_back();
T& back();
T& operator[] (size_type i);
private:
std::sharead_ptr<std::vecor<T>> data;
void check(size_type i, const std::string &msg) const;
}
12、类模板初始化需要使用显示模板参数,每个实例化的类模板实例都是一个类。
template<> class Blob<int>//显示模板参数
{
typedef typename std::vector<int>::size_type size_type;
Bolb();
Blob(std::initializer<int> il);
//...
int & operator[] (size_type i);
private:
std::shared_ptr <std::vector<int>> data;
void check(size_type i, const std:: string & msg ) const;
}
13、可以在类模板内部和外部定义成员函数,定义在内部的成员函数默认为内联函数。
14、类模板的成员函数和类模板有相同的模板参数,因此定义在模板外部的成员函数需以template开始,后接模板参数列表。
template <typename T>
ret_type Blob<T>::member-name(param-list)
15、一个类模板的成员函数只有程序用到它时才进行实例化。
16、在类模板自己的作用域中我们可以使用模板名而不提供实参,在类外需要使用类模板名带实参。
17、一个类可以将另一个模板的每个实例都声明为自己的友元,或者限定的实例为友元。为了让所有实例成为友元,友元声明中必须使用与类模板本身不同的模板参数。
template<typename T> class pal;
class c
{
friend class pal<c>;//用类c实例化的pal是c的一个友元
//pal2的所有实例都是c的友元
template<typename T> friend class pal2;
}
template <typename T> class c2
{
//c2的每个实例将相同实例化的pal声明为友元
friend class pal<T>;
//pal2的素有实例都是c2的每个实例的友元,不需要前置声明
template<typename X> friend class Pal2;
//pal3是一个非模板类,它是c2所有实例的友元
friend class pal3; // 不需要pal3的前置声明
}
18、为类模板定义类型别名
template <typename T> usign twin = pair<T,T>;
twin<string> authors;
19、模板类的每个static数据成员都必须有且只有一个成员定义。
20、在模板内不能重用模板参数名。
template <typename T> int compare(const T&,const T&);//函数模板
template <typename T> class Blob;//类模板
21、默认情况下,c++语言假定通过作用于运算符访问的名字不是类型。一般用typename显示的指定某个名字是类型。
22、对于一个模板参数而言,只有当它右侧的所有参数都有默认实参时,它才可以有默认实参。
23、使用类模板必须在模板名之后添加一个尖括号,指出类必须从一个模板实例化而来。
24、包含成员模板的类,当我们在类模板外定义一个成员模板时,必须同时为类模板和成员模板提供模板参数列表。
template <typename T> //类的类型参数
template <typename it> //构造函数的类型参数
Blob<T>::Blob:(it b, it e)
data(std::make_shared<std::vector<T>>(b,e)){}
25、当多个文件使用了相同的模板并提供了相同的模板参数时,每个文件都会有该模板的一个实例。在大系统中开销很严重,需要显示实例化。
extern template class Blob<string>;//声明
template int compare(const int & , const int&);//定义
26、当编译器遇到extern声明时,它不会在本文件中生成实例化代码。将一个实例化声明为extern表示承诺在程序的其他位置有该实例化的一个非extern声明(定义)。
27、由于编译器使用一个模板时会对其进行自动实例化,因此extern声明必须出现在任何使用此实例化版本的代码之前。
28、一个类模板的实例化定义会实例化该模板的所有成员,所以所用类型必须能用于模板的所有成员函数。
29、unique_ptr在编译时绑定删除器(避免了间接调用删除器的开销),shared_ptr在运行时绑定删除器(为用户重载提供了方便)。
#include <iostream>
30、将实参传递给带模板类型的函数形参时,能够自动应用的类型转换只有const转换及数组或函数到指针的转换(形参不能是引用)。
31、当一个模板类型参数用作多个函数形参的类型,传给它们的实参必须是相同类型的。
32、函数模板显示实参(函数返回类型不在参数列表中,需要显示指定实参),显示模板实参按照由左到有的顺序与模板参数匹配。
auto val3= sum<long long>(i,lng);//显示函数模板返回类型的实参
33、正常的类型转换应用于显示指定德实参。
34、尾置返回类型,用于放在函数参数列表之后声明返回类型。
template <typename It>
auto fcn(It beg, It end)-> decltype(*beg)
{
return *beg;
}
35、当我们用一个函数模板初始化一个函数指针或为一个函数指针赋值时,编译器使用指针的类型来推断模板实参。
36、接受右值引用参数的模板函数
template <typename T> void f3(T&& val)
{
T t=val;
t=fcn(t);
fi(val==t){}
}
37、可以利用标准库函数move获得一个绑定到左值上的右值引用。
38、可以用static_cast显示的将一个左值转换为一个右值引用。
39、一个可变参数模板就是一个接受可变数目参数的模板函数或模板类,可变数目的参数称为参数包。
template <typename T, typename...Args>//Args表示零个或多个模板类型参数
void foo(const T & t,const Args&...reset);//reset表示一个或多个函数参数
40、当我们既不知道想要处理的实参的数目也不知道他们的类型时,可变参数是很有用的。
41、可变参数函数通常是递归的。