C++第九讲:模板进阶
C++第九讲:模板进阶
- 1.非类型模板参数
- 2.模板的特化
- 3.类模板的特化
- 3.1全特化
- 3.2偏特化
- 3.2.1将模板参数表中的一部分参数特化
- 3.2.2参数进一步的限制
- 4.模板分离编译
- 4.1什么是分离编译
- 4.2为什么模板类不能够实现分离编译
- 5.模板总结
1.非类型模板参数
我们平时定义的模板参数都是类型模板参数,而非类型模板参数值的是整形常量,在C++20标准中,也支持了浮点数的定义:
//非类型模板参数
//比如说我们要定义一个静态的栈:
template<class T, size_t N = 10>
//template<class T, double N = 10>//err:常量表达式不是整形
//template<class T, char N = 10>
class Stack
{
private:
T _a[N];
int top;
};
int main()
{
Stack<int, 20> st1;
Stack<int> st2;
Stack<int, 100> st3;
return 0;
}
比如说我们昨天看的那个deque中就看到了非类型模板参数的应用
C++11也新加了一个array的东西,这个array是一个静态数组,我们看下面:
2.模板的特化
//我们实现了一个很简单的比较函数模板
template<class T>
bool Less1(T left, T right)
{
return left < right;
}
int main()
{
cout << Less1(1, 2) << endl;//可以正常比较
Date d1(2023, 10, 11);
Date d2(2023, 12, 11);
cout << Less1(d1, d2) << endl;//可以正常比较
return 0;
}
但是,我们这样改一下:
int main()
{
cout << Less1(1, 2) << endl;//可以正常比较
Date d1(2023, 10, 11);
Date d2(2023, 12, 11);
cout << Less1(d1, d2) << endl;//可以正常比较
Date* p1 = new Date(2024, 10, 11);
Date* p2 = new Date(2024, 12, 11);
cout << Less1(p1, p2) << endl;
return 0;
}
可以看出,这就成了比较地址了,就没有正确性可言了,所以我们要使用模板的特化来解决这个问题:
//我们实现了一个很简单的比较函数模板
template<class T>
bool Less1(T left, T right)
{
return left < right;
}
//模板的特化
template<>
bool Less1<Date*>(Date* d1, Date* d2)
{
return *d1 < *d2;
}
int main()
{
cout << Less1(1, 2) << endl;//可以正常比较
Date d1(2023, 10, 11);
Date d2(2023, 12, 11);
cout << Less1(d1, d2) << endl;//可以正常比较
Date* p1 = new Date(2024, 10, 11);
Date* p2 = new Date(2024, 12, 11);
cout << Less1(p1, p2) << endl;
return 0;
}
但是特化有一个比较麻烦的点:
//我们实现了一个很简单的比较函数模板
template<class T>
bool Less1(const T& left, const T& right)
{
return left < right;
}
//模板的特化
template<>
bool Less1<Date*>(Date* d1, Date* d2)//err:不是函数模板的专用化
{
return *d1 < *d2;
}
当我们Less1的实现参数为const &类型时,会发生报错,因为特化要保持和模板的性质类似,那么我们可能会这样写:
//模板的特化
template<>
bool Less1<Date*>(const Date*& d1, const Date*& d2)//err:不是函数模板的专用化
{
return *d1 < *d2;
}
还是不对,因为const Date*& d1中的const修饰的是*d1,而模板参数中的const修饰的是left本身,所以我们要将const改一个位置:
//模板的特化
template<>
bool Less1<Date*>(Date* const& d1, Date* const& d2)//err:不是函数模板的专用化
{
return *d1 < *d2;
}
3.类模板的特化
3.1全特化
如果我们将模板参数全部都特化的话,就叫做全特化:
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<T1, T2>
Date<int, char> d2;//Date<int, char>
return 0;
}
3.2偏特化
偏特化就是特化部分参数,有两种:
3.2.1将模板参数表中的一部分参数特化
//将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
Data() { cout << "Data<T1, int>" << endl; }
private:
T1 _d1;
int _d2;
};
3.2.2参数进一步的限制
偏特化不仅仅是指特化部分参数,还可以是对参数的限制:
//两个参数偏特化为指针类型
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;
};
void test2()
{
Data<double, int> d1;//调用特化的int版本
Data<int, double> d2;//调用基础的模板
Data<int*, int*> d3;//调用特化的指针版本
Data<int&, int&> d4(1, 2);//调用特化的指针版本
}
4.模板分离编译
4.1什么是分离编译
我们在实现之前的类之前,都是按照声明和定义分离分离来实现的,但是模板类的实现时,会有问题,我们下面来看一下模板类实现问题的原因