ToB企服应用市场:ToB评测及商务社交产业平台

标题: 初识C++ · 模板进阶 [打印本页]

作者: 我爱普洱茶    时间: 2024-6-8 06:37
标题: 初识C++ · 模板进阶
目录
前言:
1 非类型模板参数
2 按需实例化
3 模板特化
4 模板的分离编译

前言:

前面模板我们会了简单的使用,这里带来模板的进阶,当然,也就那么几个知识点,并不太难。

1 非类型模板参数

先来看这样一段代码:
  1. #define N 100
  2. template<class T>
  3. class Arr
  4. {
  5. public:
  6. private:
  7.         T _arr[N];
  8. };
复制代码
如果我们想要创建一个整型数组,可以使用这个类来创建,但是我们面临一个问题就是该数组的大小是固定的,我们想要简单控制这个数组的大小,可以使用宏,但是还是不够简便,因为宏不方便调试不说,实际上也是指定了大小,那么我们想要使用一个类,来创建不同大小的数组该怎么办?
这里使用到的就是非类型模板参数,如下:
  1. template<class T,size_t N = 100>
  2. class Arr
  3. {
  4. public:
  5. private:
  6.         T _arr[N];
  7. };
  8. int main()
  9. {
  10.         Arr<int,10> a1;
  11.         Arr<int,1000> a2;
  12.         Arr<int> a3;
  13.         return 0;
  14. }
复制代码
这里就得到了我们想要的不同大小的数组,那么,来个问题,编译器一共实现了几个类?
答:编译器这里一共实现了3个类,编译器根据模板参数的不同,就实现了不同的类。这里的非类型模板参数,我们可以理解为常量,如这里的N,但是在C++11只支持整型,连浮点数都不可以,只支持整型,比如int size_t char一类的,在C++ 20之后才可以支持其他类型。
这里涉及到了数组,那么引入一个小的知识点就是对于越界来说,array 数组 vector有着不同的反应:
  1. int main()
  2. {
  3.         int arr[10];
  4.         arr[10];
  5.         arr[15] = 1;
  6.         return 0;
  7. }
复制代码
对于普通数组来说,普通的越界只读来说,比如arr[10]是检查不来错误的,越界写来说,也是很多抽查不出来的,比如这段代码在vs2019上就不会报错。
那么对于array来说:
  1. int main()
  2. {
  3.         std::array<int,10> array;
  4.         array[10];
  5.         return 0;
  6. }
复制代码
任何读写越界都会报错,但是呢,这是c++委员会后面加的,但是挺鸡肋的,因为我们有vector。
  1. int main()
  2. {
  3.         std::vector<int> v;
  4.         v.reserve(1000);
  5.         return 0;
  6. }
复制代码
vector对越界的读写都会报错,这是一方面,其次是array是静态的数组,也就是大小定了,并且,它所属的空间是栈,栈的空间相对堆来说就会小很多,所以面临开大空间的时候,array就不吃香了,vector没事,因为可以动态开辟。

2 按需实例化

先看这样一段代码:
  1. template<class T,size_t N = 100>
  2. class Arr
  3. {
  4. public:
  5.         T& operator[](size_t i)
  6.         {
  7.                 size(1);
  8.                 return _arr[i];
  9.         }
  10.         size_t size()
  11.         {
  12.                 return _size;
  13.         }
  14.         bool empty()
  15.         {
  16.                 return _size == 0;
  17.         }
  18. private:
  19.         T _arr[N];
  20.         size_t _size = 0;
  21. };
  22. int main()
  23. {
  24.         Arr<int> a1;
  25.         a1.empty();
  26.         return 0;
  27. }
复制代码
从语法层面来说,size()函数没有参数,那么我们传参数的话就会导致报错对吧?可是,实际上:

代码是没有报错,也就是说size()传参数是对的吗?
不,这是因为按需实例化
在主函数里面,我们实例化了a1,并且调用了empty函数,但是我们没有调用operator[]函数,那么编译器就不会实例化operator函数,因为我们没有调用,既然没有实例化size函数,那么传什么都不会报错,这就是按需实例化
再细节一点来说,编译器会根据模板实例化->实例化一个半成品模板->再实例化为一个具体的类或者函数->最后才是语法编译,所以没有语法报错。

3 模板特化

特化我们可以理解为特殊化处理,比如我们在栈和队列的时候实现的日期类的比较,就可以不用仿函数来实现比较,可以用特化来处理。
日期类:
  1. class Date
  2. {
  3. public:
  4.         friend ostream& operator<<(ostream& _cout, const Date& d);
  5.         Date(int year = 1900, int month = 1, int day = 1)
  6.                 : _year(year)
  7.                 , _month(month)
  8.                 , _day(day)
  9.         {}
  10.         bool operator<(const Date& d)const
  11.         {
  12.                 return (_year < d._year) ||
  13.                         (_year == d._year && _month < d._month) ||
  14.                         (_year == d._year && _month == d._month && _day < d._day);
  15.         }
  16.         bool operator>(const Date& d)const
  17.         {
  18.                 return (_year > d._year) ||
  19.                         (_year == d._year && _month > d._month) ||
  20.                         (_year == d._year && _month == d._month && _day > d._day);
  21.         }
  22. private:
  23.         size_t _year;       
  24.         size_t _month;
  25.         size_t _day;
  26. };
  27. ostream& operator<<(ostream& _cout, const Date& d)
  28. {
  29.         _cout << d._year << "-" << d._month << "-" << d._day;
  30.         return _cout;
  31. }
复制代码
  1. template<class T>
  2. bool less(const T& a,const T& b)
  3. {
  4.         return a < b;
  5. }
  6. int main()
  7. {
  8.         Date d1(2024, 5, 20);
  9.         Date d2(2024, 5, 21);
  10.         cout << (d1 < d2) << endl;
  11.         return 0;
  12. }
复制代码
比如,使用函数模板,对于重载了比较符号的比较是没问题的,如果使用指针就会报错,因为默认是按照指针比较的,这里就可以使用特化:
  1. int main()
  2. {
  3.         Date* p1 = new Date(2020, 1, 1);
  4.         Date* p2 = new Date(2020, 1, 2);
  5.         cout << (p1 < p2) << endl;
  6.         return 0;
  7. }
复制代码
这段代码是有问题的是不用多说的,下面是解决方案:
  1. template<class T>
  2. bool Less(T left, T right)
  3. {
  4.         cout << "bool Less(T left, T right)" << endl;
  5.         return left < right;
  6. }
  7. template<>
  8. bool Less<Date*>(Date* p1,Date* p2)
  9. {
  10.         return *p1 < *p2;
  11. }
  12. int main()
  13. {
  14.         Date d1(2022, 7, 7);
  15.         Date d2(2022, 7, 8);
  16.         Date* p1 = &d1;
  17.         Date* p2 = &d2;
  18.         cout << Less(p1, p2) << endl;
  19.         return 0;
  20. }
复制代码
这里的语法就比较怪了,现在使用的是函数模板,有点像函数重载的感觉,当然,我们也可以直接重载一个出来:
  1. bool Less(Date* left, Date* right)
  2. {
  3.         return *left < *right;
  4. }
复制代码
那么,调用是怎么调用的呢?

这里就和半成品,成品是一个道理,重载的函数就相当于成品,特化的函数快成品了,模板就是个半成品,调用的顺序也就说的通了。
以上是函数模板的特化,看起来就像是函数重载,接着是类中的模板特化:
  1. //普通
  2. template<class T1,class T2>
  3. class Data
  4. {
  5. public:
  6.         Data() { cout << "Data<T1, T2>" << endl; }
  7. };
  8. //全特化
  9. template<>
  10. class Data<int, char>
  11. {
  12. public:
  13.         Data() { cout << "Data<int, char>" << endl; }
  14. };
  15. //偏特化
  16. template<class T1>
  17. class Data<T1, char>
  18. {
  19. public:
  20.         Data() { cout << "Data<T1, char>" << endl; }
  21. };
复制代码
使用方式和函数模板其实是差不多的,在特化这里分为全特化和偏特化,特化也不是什么特别的东西,其实就是对参数的进一步限制而已。

4 模板的分离编译

使用模板的时候,定义和声明最好放在一个文件,.h和.c文件分离会报错的,这里简单举个例子:
  1. // a.h
  2. template<class T>
  3. T Add(const T& left, const T& right);
  4. //a.cpp
  5. template<class T>
  6. T Add(const T& left, const T& right)
  7. {
  8. return left + right;
  9. }
复制代码
在调用这个函数的时候就会报错,只需要想清楚一个简单的问题就可以了,两个T是不是一样的T,能否用.h文件里面的T去平替.cpp里面的T,当然是不可以的,所以这里,就会报错,报的是链接错误,.h文件编译成功后,.cpp里面的文件是没有编译好的,因为T不知道是什么类型,调用的时候就会报错。
在链接阶段,编译器按照修饰好之后的函数名在符号表里面寻找函数,不知道类型就没有生成修饰好的函数名,那么就会报如下类似的错:


感谢阅读!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4