• c++ —— 模板的进阶使用



    前言: 模板是常考点的,内容知识点也很多,本文意在总结模板的高级使用,至于初级使用可以查看我以前的博客 —— 模板初阶的使用


    1. 非类型的模板参数

    模板的参数一定是类型吗?答案:不是。

    模板参数:

    • 类型模板参数: class或typename 后为类型
    • 非类型模板参数:可以是常量整数 int或size_t ,包括枚举常量等 所以浮点数,字符型,自定义类类型都是不能做非类型参数的

    举例:
    我们可以搞一个模板类型的静态数组。

    template <class T, size_t n = 10>
    class my_array
    {
    public:
    	my_array()
    		:_size(n)
    	{
    
    	}
    	
    	T& operator [](size_t index)
    	{
    		return My_array[index];
    	}
    
    	size_t size()
    	{
    		return _size;
    	}
    
    	bool empty()
    	{
    		return _size == 0;
    	}
    		
    private:
    	T My_array[n];
    	
    	size_t _size;
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    这个数组的默认大小我设置为10,当然我们可以自己来定义数组的大小,

    // 默认是10
    my_array<int> a;
    // 传模板参数100
    my_array<int,100> b;
    
    • 1
    • 2
    • 3
    • 4

    我们也可以验证一下,如果非类型模板参数我们不设置为常量,而是设置成float,看看会是什么情况

    在这里插入图片描述
    c++20,会支持非类型模板参数为float,之前的版本都不支持的。


    2. 模板的特化

    2.1 什么是模板特化

    模板的特化:

    • 函数模板特化
    • 类模板特化

    特化的目的是实现特殊的功能,如果按照原本模板的推导,并不能实现我们预期的功能,这就需要我们进行模板特化。

    定义:
    模板特化(template specialization)不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化。 模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。

    2.2 函数模板特化

    比如我们要进行一个比大小的功能,如果传的是int ,long 等都还好说,要是传的是一个int* 指针,我们改怎么操作呢?比地址的高低是没有意义的,所以我们要去比 int*所以指向的内容。

    template<class T>
    
    bool compare(T& x,T& y)
    {
    	return x > y;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这就是一个简单的比较大小的函数模板:

    int main()
    {
    	cout << compare(10, 2) << endl;
    	cout <<compare(2, 10)<<endl;
    	int a = 10;
    	int b = 2;
    	int* x = &a;
    	int* y = &b;
    	cout << compare(a, b) << endl;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    前俩个compare()是不需要函数特化的,模板去推导的就满足功能;但是最后的compare(),比较的是指针,模板推导比的是地址的高低,我想要去比较指针所指向的内容,该怎么办?模板特化。

    template<class T>
    bool compare( T x, T y)
    {
    	return x > y;
    }
    
    template<>
    bool compare<int*>(int* x,int* y)
    {
    	return *x > *y;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这就是函数模板的特化。

    函数模板特化的使用规则:

    1. 必须要先有一个基础的函数模板
    2. 关键字template后面接一对空的尖括号<>
    3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
    4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪错误

    2.3 类模板的特化

    类模板的特化分类:

    • 全特化:类模板的参数全部确定化
    • 偏特化:类模板的参数部分确定化,类模板的参数进一步进行限制。

    (1) 全特化

    我拿下面的类进行举例:

    template<class T1,class T2>
    class two_val
    {
    public:
    	two_val()
    	{
    		cout << "" << endl;
    	}
    private:
    	T1 x;
    	T2 y;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    全特化为类模板的参数全部确定化

    template<>
    class two_val<char,int>
    {
    public:
    	two_val()
    	{
    		cout << "" << endl;
    	}
    private:
    	char x;
    	int y;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们来创建类对象:

    int main()
    {
    	two_val<int, int> s;
    	two_val<char, int> ss;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    (2) 偏特化
    偏特化首先可以只特化一部分:
    以下两种都是偏特化:

    template<class T1>
    class two_val<T1,int>
    {
    public:
    	two_val()
    	{
    		cout << "" << endl;
    	}
    private:
    	T1 x;
    	int y;
    };
    
    template<class T2>
    class two_val<char,T2>
    {
    public:
    	two_val()
    	{
    		cout << "" << endl;
    	}
    private:
    	char x;
    	T2 y;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    偏特化其次是一种对特化的进一步限制:
    比如:将模板参数,特化成指针 / 特化成引用,这也是一种偏特化

    template<class T1, class T2>
    class two_val<T1*,T2*>
    {
    public:
    	two_val()
    	{
    		cout << "" << endl;
    	}
    private:
    	T1* x;
    	T2* y;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3. 模板的分离编译

    为什么要分离编译?我们在做一个较大的项目时,多人协作,头文件一般用于看框架,源文件去实现功能,这个功能的实现需要多人协作,每个人负责实现一部分的功能。

    但是模板声明和定义分开的话,会导致链接错误。这是因为分开定义导致模板函数或类,没有实例化。

    我可以演示一下:
    以一个加法模板函数为例:

    add.h头文件:

    #pragma once
    template <class T>
    T add(T x, T y);
    
    • 1
    • 2
    • 3

    add.c源文件:

    #include"add.h"
    template <class T>
    T add(T x, T y)
    {
    	return x + y;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    main函数源文件:

    #include"add.h"
    int main()
    {
    	add(1, 2);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们看看运行情况:
    很明显的链接错误、
    在这里插入图片描述
    该如何解决呢?

    1. 显示的实例化:链接错误的原因是没有实例化,那么我们可以手动实例化。
      在头文件中加入我们具体的实例化:

    add.h头文件:

    #pragma once
    template <class T>
    T add(T x, T y);
    
    template
    int add<int>(int x, int y);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这样就支持了 int类型的加和。如果想要支持double类型的加和呢?还得手动实例化,这种方法是比较累人的。

    1. 在头文件中定义,使得声明和定义不分离

    这种方法是常用的,定义在头文件里就好了,简单省事:

    #pragma once
    
    template <class T>
    T add(T x, T y);
    
    template <class T>
    T add(T x, T y)
    {
    	return x + y;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    类模板和函数模板是一样的。


    4. 模板总结

    优点:

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

    缺陷:

    1. 模板会导致代码膨胀问题,也会导致编译时间变长
    2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误
  • 相关阅读:
    FDTD script command (对结构/数据操作)
    Python少儿编程小课堂(七)入门篇(7)for 循环语句
    优先级队列(堆)——小记
    【记录】Truenas scale|Truenas 的 SSH 服务连不上 VScode,终端能连上
    【STM32F407+CUBEMX+FreeRTOS+lwIP之UDP记录】
    尚硅谷-云尚办公-项目复盘
    【华为OD机试真题 python】数组二叉树【2022 Q4 | 200分】
    Android UI 开发·界面布局开发·案例分析
    LeetCode刷题日记:135. 分发糖果
    前端第二课,HTML,alt,title,width/heigh,border,<a>超链接,target,tr,td,th
  • 原文地址:https://blog.csdn.net/lyzzs222/article/details/126863786