目录
概述
C++模板是一种强大的功能,它允许开发者编写泛型代码,从而提高代码的重用性和灵活性。本文将探讨非类型模板参数的概念及其限制,以及模板特化的方法,包括函数模板特化和类模板特化。
1. 非类型模板参数
非类型模板参数是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。非类型模板参数必须在编译期就能确认结果,并且有一些限制,例如:
- 浮点数、类对象以及字符串不允许作为非类型模板参数。
- 非类型的模板参数必须在编译期就能确认结果。
template<class T,size_t size = 10>
int add(T a1) {
return a1 + size;
}
template<class T,size_t N = 10>
class MyClass
{
public:
MyClass(const T& val)
:arr[0] = val;
{}
private:
int arr[N];
};
void test1() {
//cout << add<>(10) << endl;
MyClass<int> s1(10);
}
int main() {
test1();
return 0;
}
2. 模板的特化
模板特化是在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化分为函数模板特化与类模板特化。
2.1 函数模板特化
函数模板特化是当函数模板在处理某些特定类型时可能无法得到预期结果的情况下使用的。特化过程包括以下几个步骤:
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)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
class DateLess
{
public:
//bool operator()(Date* p1, Date* p2)
//{
// return *p1 < *p2;
//}
};
//模板特化
template<class T>
bool lessfunc(const T& left, const T& right) {
return left < right;
}
//特化--不推荐
//template<>
//bool lessfunc<Date*>(Date* const& left, Date* const& right)
//{
// return *left < *right;
//}
//
//template<>
//bool lessfunc<const Date*>(const Date* const& left,const Date* const& right)
//{
// return *left < *right;
//}
//推荐直接写成函数
bool lessfunc(Date* p1, Date* p2) {
return *p1 < *p2;
}
bool lessfunc(const Date* p1,const Date* p2) {
return *p1 < *p2;
}
void test2() {
//cout << lessfunc(1, 2) << endl;
//int a = 100, b = 20;
//int* pa = &a;
//int* pb = &b;
//cout << lessfunc(pa,pb) << endl;//比较错误
Date d1(2025, 7, 20);
Date d2(2022, 7, 8);
cout << lessfunc(d1, d2) << endl;
Date* p1 = &d1;
Date* p2 = &d2;
cout << lessfunc(p1, p2) << endl;//比较错误
}
int main() {
test2();
return 0;
}
2.2 类模板特化
类模板特化分为全特化和偏特化。
2.2.1 全特化
全特化是将模板参数列表中所有的参数都确定化。
//全特化
template<>
class Data<int,int>
{
public:
Data() { cout << "data<int,int>" << endl; }
};
2.2.2 偏特化
偏特化是指对模板参数进行进一步的条件限制设计出的特化版本。偏特化有两种表现形式:部分特化和参数更进一步的限制。
//偏特化/半特化//全特化
template<class T1>
class Data<T1, int>
{
public:
Data() { cout << "data<T1,int>" << endl; }
};
参数更进一步的限制 是针对模板参数的类型进行更严格的限制。例如,将两个参数特化为指针类型:
template<>
class Data<int*, int*>
{
public:
Data() { cout << "data<int*,int*>" << endl; }
};
template<>
class Data<int&, int&>
{
public:
Data() { cout << "data<int&,int&>" << endl; }
};
类模板特化应用示例
我们可以通过一个例子来看一下如何使用类模板特化来解决特定的问题。假设我们有一个用于比较的类模板 Less
,它可以用于直接比较日期对象,但是当比较指针时则会比较指针的地址而非指针指向的内容。我们可以使用类模板特化来解决这个问题。
#include<algorithm>
#include<vector>
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)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
template<class T>
class Less {
public:
//仿函数
bool operator()(const T& a1,const T& a2) {
return a1 > a2;
}
};
//特化
template<class Data>
class Less <Data*>{
public:
//仿函数
bool operator()(Data* a1, Data* a2) const{
return *a1 > *a2;
}
};
int main() {
Date d1(2024, 1, 1);
Date d2(2024, 11, 1);
Date d3(2024, 10, 1);
//vector<Date> s1;
//s1.push_back(d1);
//s1.push_back(d2);
//s1.push_back(d3);
//sort(s1.begin(), s1.end(), Less<Date>());
vector<Date*> s1;
s1.push_back(&d1);
s1.push_back(&d2);
s1.push_back(&d3);
sort(s1.begin(), s1.end(), Less<Date*>());
return 0;
}
3. 模板分离编译
分离编译模式是指一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程。
在使用模板时,如果模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义,则需要特别注意编译过程。解决方法包括:
- 将声明和定义放到一个文件
"xxx.hpp"
或者"xxx.h"
中。这是推荐的做法。 - 显式实例化模板定义的位置。这种方法不实用,不推荐使用。
解决方法
总结
模板是C++语言的重要特性,它提供了代码重用的强大手段。然而,使用模板也需要注意其局限性,如非类型模板参数的限制、模板特化的正确使用等。通过合理利用模板特化,我们可以解决特定类型的问题,使得代码更加灵活和高效。