🌈个人主页:羽晨同学
💫个人格言:“成为自己未来的主人~”
非类型模版参数
模板参数分类型模板参数与非类型模板参数
类型形参即:出现在模板参数列表,跟在class或者typename之类的参数类型名称
非类型形参即:就是用一个常量作为类(函数)模版的一个参数,在类(函数)模版中可将该参数当成常量来使用
int main()
{Stack<int> st1; //10Stack<int,100> st1; //100Stack<int,1000> st1; //1000return 0;
}
首先,我们想在定义的类中实现这样的功能,很明显,这是不可能的,但是有了非类型模板参数之后,这就成为了可能,那应该怎么做呢?
#define _CRT_SECURE_NO_WARNINGS
#include<stack>
#include<iostream>
using namespace std;
template<class T,size_t N=10>
class Stack
{
public:void func(){N++;}
private:int _a[N];int _top;
};
int main()
{Stack<int> st1; //10Stack<int,100> st1; //100Stack<int,1000> st1; //1000return 0;
}
你看, 这样的话,我们就可以通过改变传递的参数来改变N的值
需要注意的是,C++20之前,浮点数是不允许作为非类型模版参数的
但是在C++20当中,它是可以的。
template<double X,int*ptr>
class A
{};
但是类对象和字符串是不允许的
array
array相较于vector而言,其实它具备更严格的越界检查。
#include<array>
#include<vector>
int main()
{//严格越界检查array<int, 10> aa1;cout << sizeof(aa1) << endl;aa1[10];aa1[14] = 1;array不如用vector。鸡肋的设计//vector<int> v1(10, 1);//v1[14];//cout << sizeof(v1) << endl;return 0;
}
这里其实就体现出来了一部分array的特征
int aa2[10];aa2[13];
你看,在这个代码中,就不会进行报错的,这是因为在静态变量中, 执行的是随机抽样检测,所以后面是不会报错的。
我们再来看一下vector的越界部分,在vector当中,同样也会进行严格的越界检查,溢出也会进行报错。
但是,array所花费的内存比vector更大:
所以说,其实array是完全鸡肋的设计
你看,下面的这段代码是我们所实现的一个打印int的打印函数
void PrintVector(const vector<int>& v)
{vector<int>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;
}
在这个代码中,我们所能打印的只是int类型的变量,但是我们想要打印其他的类型,我们应该怎么做呢,最简单的,引入模版的概念。
我们来看下面的这一段代码:
template<class T>
void PrintVector(const vector<T>& v)
{vector<T>::const_iterator it = v.begin();while (it != v.end()){cout << *it << endl;++it;}cout << endl;
}
int main()
{vector<double> v2{1.1, 2.2, 1.3, 6.6, 7.7, 8.8};PrintVector(v2);return 0;
}
但是很抱歉的是,这样代码是会进行报错的,这是为什么呢?
这是因为类模板在没有实例化之前,是不会去里面查具体的东西的,这就导致了编译器不知道这个是vector<T>::const_iterator it类型还是变量 。
而在这个时候,我们就会在代码的前面加一个typename来告诉编译器这是一个类型
template<class T>
void PrintVector(const vector<T>& v)
{typename vector<T>::const_iterator it = v.begin();while (it != v.end()){cout << *it << endl;++it;}cout << endl;
}
int main()
{vector<double> v2{1.1, 2.2, 1.3, 6.6, 7.7, 8.8};PrintVector(v2);return 0;
}
特化
接下来,让我们看一下要实现的比较日期类的函数
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(const T& left, const T& right)
//{
// return left < right;
//}
bool Less(Date* left, Date* right)
{return *left < *right;
}
你看,在这段代码中,我们实现了一个小于的比较,这里能够实现是因为我们对这个<进行的函数重载。
通常情况下,我们使用模版可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊化处理。
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, 6, 8);cout << Less(d1, d2) << endl;Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl;return 0;
}
在这个代码当中:
我们可以看到,第三个答案是错误的。因为这个是地址的比较
此时,我们就需要对模版进行特化,即:在原模板基础上,针对特殊类型进行特殊化的处理方式。模板特化中,分为类模板特化和函数模板特化
//类模板
template<class T1,class T2>
class Data
{
public:Data(){cout << "Data<T1,T2>-原模板" << endl;}
private:T1 _d1;T2 _d2;
};
//特化:针对某种特殊类型,进行特殊化处理
//全特化
template<>
class Data<int,char>
{
public:Data(){cout << "class Data<int,char>-> 全特化 " << endl;}
};
template<class T1>
class Data<T1, int>
{
public:Data(){cout << "class Data<T1, int>->半特化" << endl;}
private:T1 _d1;int _d2;
};
上面的这个其实给出了全特化和半特化的例子,半特化中我们引出了参数,一个固定,一个随机参数。
限定模版类型
// 限定模版的类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:Data() { cout << typeid(T1).name() << endl;cout << typeid(T2).name() << endl;cout << "Data<T1*, T2*>-偏特化" << endl << endl;//T1 x1;//T1* p1;}
};template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:Data(){cout << typeid(T1).name() << endl;cout << typeid(T2).name() << endl;cout << "Data<T1&, T2&>" << endl << endl;}
private:
};template <typename T1, typename T2>
class Data <T1&, T2*>
{
public:Data(){cout << typeid(T1).name() << endl;cout << typeid(T2).name() << endl;cout << "Data<T1&, T2*>" << endl << endl;}
private:
};
好了,本次的文章就到这里了,我们下次再见。