1、非类型模板参数
2、类模板的特化
3、模板的分离编译
1、非类型模板参数
模板参数分为两个类型:
a、类类型形参:跟在class或者typename之类的后面的参数类型名称
b、非类型形参:用一个常量作为类模板的一个参数,在类模板中可以当成常量使用
#include <iostream>
using namespace std;
template<class T, size_t N = 10> // 这里给了10作为缺省值,可以不给
class array
{
private:
T _array[N];
};
int main()
{
array<int> a1; //缺省赋值,a1成员数组容量为10
array<int, 100> a2; //a2成员数组容量为100
return 0;
}
2、类模板的特化
对于一些特殊类型使用模板可能会得到错误的结论,所以需要特殊处理,例:
#include <iostream>
using namespace std;
struct Date
{
Date(int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
{}
bool operator>(const Date& d) const
{
if ((_year > d._year)
|| (_year == d._year && _month > d._month)
|| (_year == d._year && _month == d._month && _day > d._day))
{
return true;
}
else
{
return false;
}
}
bool operator<(const Date& d) const
{
if ((_year < d._year)
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && _day < d._day))
{
return true;
}
else
{
return false;
}
}
int _year;
int _month;
int _day;
};
template<class T>
bool Less(T left, T right)
{
return left < right;
}
int main()
{
cout << Less(1, 2) << endl; //可以比较,结果正确
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << Less(d1, d2) << endl; //可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; //可以比较,结果错误
return 0;
}
从上面代码看出,对于两个指针传参过去比较的结果是错误的,因为Less函数比较的是两个指针的地址,而不是它们所指向的值,于是便要对这种类型进行特化处理,这就是模板特化的意义。
模板特化分为:a、函数模板特化 b、类模板特化
a、函数模板特化
步骤:
1、先有一个基础的函数模板
2、关键字template后面接一对空的尖括号<>
3、函数名后面跟一对尖括号<>,里面指定要特化的类型
4、函数形参表:必须和模板函数的原型完全相同
#include <iostream>
using namespace std;
struct Date
{
Date(int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
{}
bool operator>(const Date& d) const
{
if ((_year > d._year)
|| (_year == d._year && _month > d._month)
|| (_year == d._year && _month == d._month && _day > d._day))
{
return true;
}
else
{
return false;
}
}
bool operator<(const Date& d) const
{
if ((_year < d._year)
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && _day < d._day))
{
return true;
}
else
{
return false;
}
}
int _year;
int _month;
int _day;
};
template<class T>
bool Less(T left, T right)
{
return left < right;
}
template<> //特化Date*类型
bool Less<Date*>(Date* left, Date* right)
{
return *left < *right;
}
int main()
{
cout << Less(1, 2) << endl; //可以比较,结果正确
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << Less(d1, d2) << endl; //可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; //可以比较,结果错误
return 0;
}
这样传指针比较的时候结果便不会错误了。
b、类模板特化
全特化:将模板参数列表中的所有参数都确定化。
#include <iostream>
using namespace std;
template<class T1, class T2>
class Date
{
public:
Date() { cout << "Date<T1, T2>" << endl; }
private:
T1 _d1;
T2 _d2;
};
template<> //全特化,参数列表中都是确定的类型
class Date<int, char>
{
public:
Date() { cout << "Date<int, char>" << endl; }
private:
int _d1;
char _d2;
};
int main()
{
Date<int, int> d1; //调用原类模板
Date<int, char>d2; //调用全特化类模板
return 0;
}
偏特化(半特化):特化部分参数或者对原模板参数进行进一步的限制:
#include <iostream>
using namespace std;
template<class T1, class T2>
class Date
{
public:
Date() { cout << "Date<T1, T2>" << endl; }
private:
T1 _d1;
T2 _d2;
};
template<class T1, class T2>
class Date<class T1*, class T2*> //对原模版参数的进一步限制
{
public:
Date() { cout << "Date<T1*, T2*>" << endl; }
private:
T1* _d1;
T2* _d2;
};
template<class T1>
class Date<class T1, int> //特化部分参数
{
public:
Date() { cout << "Date<T1, int>" << endl; }
private:
T1 _d1;
int _d2;
};
int main()
{
Date<int, int> d1; //调用原类模板
Date<int*, int*> d2; //调用特化的指针版本
Date<double, int> d3; //调用特化的int版本
return 0;
}
3、模板的分离编译
一个项目由若干个源文件共同实现,而每个源文件单独编译形成目标文件,最后将所有的目标文件链接起来形成单一的可执行文件的过程称为分离编译模式,一般在C语言中经常使用,而在C++中可能会有问题。
C/C++程序运行要经历四个步骤:预处理->编译->汇编->链接
编译:对程序按照语言特性进行词法、语法、语义分析,错误检查无误后生成汇编代码,头文件不参与编译,编译器对工程中的多个源文件是分离开单独编译的。
链接:将多个obj文件合并成一个,并处理没有解决的地址问题。
解决方法:将声明和定义放到一个文件里。