目录
01.非类型模版参数
02.模版的特化
函数模版的特化
类模版特化
01.非类型模版参数
定义:
模版参数分为类型参数与非类型参数
类型形参:跟在class或typename后面的参数类型名称,例如:
template <class T1, typename T2>
void printTypes() {
std::cout << "Type 1: " << typeid(T1).name() << std::endl;
std::cout << "Type 2: " << typeid(T2).name() << std::endl;
}
非类型形参:可以是整型、指针、引用、枚举等等,例如:
template <int N>
void printNTimes(const std::string& str) {
for (int i = 0; i < N; ++i) {
std::cout << str << std::endl;
}
}
此处N就是非类型模版参数,它是一个整数。
常量表达式:
非类型模版参数必须是一个常量表达式,这意味着在编译时,必须能够确定其值
template<typename T, int N>
void print(vector<T> arr)
{
for (int i = 0; i < N; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
vector<int> arr{ 1,2,3,4,5,6,7,8,9,10 };
print<int>(arr);
}
此时编译不通过,因为没有定义N的值。也可以通过缺省参数的形式为N赋上初始值:
template<typename T, int N = 10>
void print(vector<T> arr)
{
for (int i = 0; i < N; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
vector<int> arr{ 1,2,3,4,5,6,7,8,9,10 };
print<int>(arr);
}
多个非类型参数
模版类型形参中可以只有非类型参数,并且可以有多个,例如:
template<int weight = 5, int height = 5>
void print2()
{
for (int i = 0; i < height; i++) {
for (int j = 0; j < weight; j++) {
cout << "*" <<" ";
}
cout << endl;
}
}
int main()
{
print2<5,10>();
}
这里的两个非类型参数分别表示打印矩阵的宽和高。
类型推导
在C++17中,非类型模版参数可以被推导:
template <auto Value>
void printValue() {
std::cout << Value << std::endl;
}
printValue<42>();
这里的Value会被推导成int类型。
02.模版的特化
函数模版的特化
使用模版可以实现一些与类型无关的代码,但是对于一些特殊类型可能就会得到与预期不一致的结果,需要进行特殊化处理,比如:实现了一个用于"<"比较的模版函数:
template<typename T>
bool Less(T left, T right)
{
return left < right;
}
class Date
{
public:
Date(int year ,int month ,int day)
:_year(year)
,_month(month)
,_day(day)
{}
bool operator<(Date& other)
{
if (_year != other._year) return _year < other._year;
if (_month != other._month) return _month < other._month;
return _day < other._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
cout << Less(1, 2) << endl; // 参数为整形,结果1
Date d2(2004, 8, 22);
Date d1(2024, 5, 8);
cout << Less(d1,d2) <<endl; // 参数为类,结果0
cout << Less(&d1, &d2) << endl; // 参数为类指针,结果1
}
可以看出,对int类型和Date类型的比较结果都正确,但是对于Date*类型,结果就出现了错误,这是因为编译器是对两者的地址大小进行比较,而我们希望对其本身进行比较,此时就需要对Date*这一类型特殊化处理:
函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
template<typename T>
bool Less<Date*>(Date* left, Date* right)
{
return *left < *right;
}
类模版特化
1.全特化
全特化是指所有模版参数都进行特殊化处理:
template<typename T1, typename T2>
class Data
{
public:
Date() { cout << "Date<T1, T2>" << endl; }
private:
T1 d1;
T2 d2;
};
template<>
class Data<int, char> // 全部特殊化处理
{
public:
Data() { cout << "Date<int, char>" << endl; }
private:
int d1;
char d2;
};
2.偏特化
类模版的偏特化有两种表现形式:
- 部分特化
将一部分参数进行特化:
template<typename T1>
class Data<T1, char>
{
public:
Data() { cout << "Date<int, char>" << endl; }
private:
T1 d1;
char d2;
};
- 参数更进一步限制
偏特化还指对模版参数进行更近一步的条件限制:
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
Data(const T1& d1, const T2& d2)
: _d1(d1)
, _d2(d2)
{
cout<<"Data<T1&, T2&>" <<endl;
}
private:
const T1 & _d1;
const T2 & _d2;
};
这里并没有将参数特化成具体的数据类型,而是特化为指针、引用之类的复合类型。
总结:
模板特化的作用在一般模板无法满足特定类型或特定值的需求时,提供针对特定类型或特定值的定制化实现。
以上就是关于模版的一些补充知识的整理了,欢迎在评论区留言,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉