• C++模版进阶


    一、非类型模版参数

    之前学习的模版,参数一般是某种类型,但其实非类型的参数也可以定义在模版里面,但也有一定的限制,只可以定义整形家族的参数,而且具有常量性

    注意:

    1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。

    2. 非类型的模板参数必须在编译期就能确认结果。

    1. namespace chk
    2. {
    3. // 定义一个模板类型的静态数组
    4. template<class T, size_t N = 10>
    5. class array
    6. {
    7. public:
    8. T& operator[](size_t index){return _array[index];}
    9. const T& operator[](size_t index)const{return _array[index];}
    10. size_t size()const{return _size;}
    11. bool empty()const{return 0 == _size;}
    12. private:
    13. T _array[N];
    14. size_t _size;
    15. };
    16. }

    二、模板的特化

    1.概念

    模板的特化是针对一些较为特殊的情况,可以针对模板的某一种或者某一类实例化去进行特殊的处理,在某些场景下会特别方便

    例如,在使用堆时若是想传指针,实际想比较的是指针所指向的内容时,可以对模版进行特化,在针对传模板参数为两个指针时进行特殊处理

    1. // 函数模板 -- 参数匹配
    2. template<class T>
    3. bool Less(T left, T right)
    4. {
    5. return left < right;
    6. }
    7. // 对Less函数模板进行特化
    8. template<>
    9. bool Less(Date* left, Date* right)
    10. {
    11. return *left < *right;
    12. }
    13. int main()
    14. {
    15. cout << Less(1, 2) << endl;
    16. Date d1(2022, 7, 7);
    17. Date d2(2022, 7, 8);
    18. cout << Less(d1, d2) << endl;
    19. Date* p1 = &d1;
    20. Date* p2 = &d2;
    21. cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
    22. return 0;
    23. }

    2.特化

    特化分为全特化和偏特化,其目的都是为了对模版部分实例化,根据不同的要求进行特殊的处理

    特化的格式是:

    对需要进行特化处理的部分参数,在template列表里不写,不进行特化的照写,到函数名后一一对应,将特化的部分给具体值或者类型,不特化的部分照写模板,例如:

    1. template<class T,class U,char N>
    2. class chk
    3. {
    4. //...
    5. }
    6. //全特化
    7. template<>
    8. class chk<int,double,5>
    9. {
    10. //...
    11. }
    12. //偏特化U为int
    13. template<class T,char N>
    14. class chkint,N>
    15. {
    16. //...
    17. }

    (1)全特化

    全特化是指模板参数全确定的情况下,进行特殊处理,对所有模板参数都进行特化

    1. template<class T1, class T2>
    2. class Data
    3. {
    4. public:
    5. Data() {cout<<"Data" <
    6. private:
    7. T1 _d1;
    8. T2 _d2;
    9. };
    10. template<>
    11. class Data<int, char>
    12. {
    13. public:
    14. Data() {cout<<"Data" <
    15. private:
    16. int _d1;
    17. char _d2;
    18. };

    (2)偏特化

    在部分情况下会需要针对某一种具体的情况进行处理,可以用全特化,而往往过多的可以是针对某一类情况进行特殊处理,偏特化的范围是比全特化的范围更大的

    偏特化也分两种方式,一种是部分特化,一种是将模板参数进一步的限制,举个例子

    1. template<class T1, class T2>
    2. class Data
    3. {
    4. public:
    5. Data() {cout<<"Data" <
    6. private:
    7. T1 _d1;
    8. T2 _d2;
    9. };

    这是用来给下面两种特化做对比举例的类

    部分特化

    将部分模板参数特化成具体的类型或者常量,例如:

    1. // 将第二个参数特化为int
    2. template <class T1>
    3. class Dataint>
    4. {
    5. public:
    6. Data() {cout<<"Data" <
    7. private:
    8. T1 _d1;
    9. int _d2;
    10. };
    对参数进一步限制

    例如你穿过来的是指针或者是引用,则会走特化的那一块,这种特化的格式要注意,template里的参数不能省略

    1. //两个参数偏特化为指针类型
    2. template <typename T1, typename T2>
    3. class Data
    4. {
    5. public:
    6. Data() {cout<<"Data" <
    7. private:
    8. T1 _d1;
    9. T2 _d2;
    10. };
    11. //两个参数偏特化为引用类型
    12. template <typename T1, typename T2>
    13. class Data
    14. {
    15. public:
    16. Data(const T1& d1, const T2& d2)
    17. : _d1(d1)
    18. , _d2(d2)
    19. {
    20. cout<<"Data" <
    21. }
    22. private:
    23. const T1 & _d1;
    24. const T2 & _d2;
    25. };

    三、模板的分离编译

    模板通常不支持将声明和定义放在两个文件中进行编译,其原因是在链接前,两个文件分别进行预处理,编译,汇编,最后再链接,在编译阶段,由于模板的定义与声明分离,在定义部分并不会完成模板的实例化,也就无法具体的对类或函数里的内容进行编译,而在声明部分则是由于存在声明,依然通过了编译阶段,到了链接时,由于定义的部分未被实例化,因此找不到具体的地址去执行相关的函数,因此会出现链接错误

    可以使用显示实例化解决,但这种方式并不推荐

    通常我们选择将声明和定义放到同一个文件当中,较短的定义直接在类里面定义,较长的可以选择在类里面声明,在类外面(同一个文件中)定义。

    四、模板的优缺点总结

    【优点】

    1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生

    2. 增强了代码的灵活性

    【缺陷】

    1. 模板会导致代码膨胀问题,也会导致编译时间变长

    2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

  • 相关阅读:
    C#/.NET/.NET Core优秀项目和框架2023年9月简报
    【数据结构C/C++】优先(级)队列
    基于springboot的图书管理系统设计与实现
    Python入门基础篇
    nacos
    vue3输入单号和张数,自动生成连号的单号
    win10 smb共享硬盘
    MySQL5.7安装详细过程--window系统
    冰箱“扩容”的战事,在今夏格外猛烈
    前端项目面试核心问题(持续更新)
  • 原文地址:https://blog.csdn.net/china_chk/article/details/134045457