1.非类型模板参数
模板参数分为类型形参与非类型形参(都可以用缺省值)
类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称
非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用
我们定义一个静态的栈
宏
//宏#define N 10
template<class T>
class Stack
{
private:T _a[N];int _top;
};
int main()
{Stack<int>st1;Stack<int>st2;//做不到这两个栈存不一样的return 0;
}
我们使用非类型模板参数
//template<class T,size_t N>
template<class T,size_t N=10>//也可以给缺省值class Stack
{
private:T _a[N];int _top;
};
int main()
{Stack<int> st0;//10Stack<int,20> st1;//20Stack<int,200> st2;//200return 0;
}
浮点数,类对象以及字符串是不允许作为非类型模板参数的
非类型的模板参数必须在编译期就能确认结果
template<class T, double N = 10.2>class St
{
private:T _a[N];int _top;
};
2.模板的特化
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理
2.1函数模板的特化
特化不能单独存在,要在函数模板的基础上叠加
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}
//函数模板
template<class T>
bool Less(T left, T right)
{return left < right;
}
//函数模板支持特化
//特化
//不能独立存在,要在函数模板的基础上写,不能单独存在
//期望按照指向的内容去比较
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}int main()
{cout << Less(1, 2) << endl;Date d1(2022, 7, 7);Date d2(2020, 6, 6);cout << Less(d1, d2) << endl;Date* p1 = new Date(2023, 5, 2);Date* p2 = new Date(2023, 4, 2);cout << Less(p1, p2) << endl;return 0;
}
//1
//0
//0
有现成的就用现成的,也相当于特殊化处理了
//我们也可以使用普通函数
bool Less(Date* left, Date* right)
{return *left < *right;
}
2.2类模板的特化
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}template<class T1,class T2>
class Data
{
public:Data() { cout << "Date<T1,T2>" << endl; }
private:T1 _d1;T2 _d2;
};
//全特化
template<>//!!!必须有
class Data<int, int>
{
public:Data() { cout << "Data<int,int>" << endl; }
};
//偏特化 --- 特化部分参数
template <class T1>//需要一个模板参数可以留一个
class Data<T1, int>//得写全
{
public:Data() { cout << "Data<T1,int>" << endl; }
};
//偏特化 --- 两个参数偏特化为指针类型--对类型的进一步限制
template <class T1,class T2>
class Data<T1*, T2*>
{
public:Data() { cout << "Data<T1*,T2*>" << endl; }
private:
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>//也可以是template <class T1,class T2>
class Data<T1&, T2&>
{
public:Data() { cout << "Data<T1&,T2&>" << endl; }
private:
};int main()
{Data<int, int> d1;Data<int, char> d2;Data<char, char> d3;Data<char, int> d4;Data<char*, int*> d5;Data<int*, int*> d6;Data<int&, int&> d7;return 0;
}//Data<int,int>
//Date<T1,T2>
//Date<T1,T2>
//Data<T1,int>
//Data<T1*,T2*>
//Data<T1*,T2*>
//int
//int
//Data<T1&,T2&>//偏特化以后还是原类型
3.模板分离编译
分离编译:
一个程序/项目由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一得可执行文件得过程
a.h a.cpp test.cpp
预处理:展开头文件,条件编译,宏替换,去掉注释
a.cpp->a.i test.cpp->test.i
编译:检查语法,生成汇编代码
a.i->a.s test.i->test.s
汇编:汇编代码转换成二进制的机器码
a.s->a.o test.0
链接:合并在一起,并且没有确定地址的函数,要确定地址等工作生成可执行程序
a.o
---> a.out
test.o
链接时,Func可以链接成功,Add无法链接成功
test.i 中知道Add需要实例化成int和double各一份Add函数,但是他只有声明没有定义
a.i 中有Add的定义,但是不知道要实例化模板成什么类型
//a.i 定义template<class T>
T Add(const T& left, const T& right);
void Func(int x);
template<class T>
T Add(const T& left, const T& right);
{return left + right;
}
void Func(int x)
{cout << "void Func(int x)" << endl;
}//test.i 声明
template<class T>
T Add(const T& left, const T& right);
void Func(int x);
int main()
{Add(1, 2);Add(1.1, 2.2);Func(1);return 0;
}
解决:
1).cpp 中显示实例化,但是不好用
增加一个类型就不行了,每增加一个新类型就得实例化
//a.cpp#define _CRT_SECURE_NO_WARNINGS 1#include"a.h"//template<class T>
//T Add(const T& left, const T& right)//void Func(int x)template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
void Func(int x)
{cout << "void Func(int x)" << endl;
}
//显式实例化也可以实现声明和定义分离
template
int Add(const int& left, const int& right);
template
double Add(const double& left, const double& right);//a.h#pragma once#include<iostream>
using namespace std;//放声明
template<class T>
T Add(const T& left, const T& right);
template<class T>
T Add(const T& left, const T& right);
void Func(int x);//Test.cpp#define _CRT_SECURE_NO_WARNINGS 1#include"a.h"int main()
{
cout<<Add(1, 2)<<endl;
cout<<Add(1.1, 2.0)<<endl;
Func(1);
return 0;
}//3
//3.1
//void Func(int x)
2)避免声明和定义分离,直接定义在.h,不存在链接要去找的问题
这样就都可以了
//a.cpp#define _CRT_SECURE_NO_WARNINGS 1#include"a.h"void Func(int x)
{cout << "void Func(int x)" << endl;
}//a.h#pragma once#include<iostream>
using namespace std;//放声明
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
void Func(int x);//Test.cpp#define _CRT_SECURE_NO_WARNINGS 1#include"a.h"int main()
{
cout<<Add(1, 2)<<endl;
cout<<Add(1.1, 2.0)<<endl;
cout << Add('a', 'x') << endl;
Func(1);
return 0;
}