再谈c++模板

前言

在前面我们曾经简单的介绍过c++的模板,但还并不全面,我们通过stl容器的学习加深了我们对c++这门语言的理解。那么今天我们就再来谈一谈c++模板,对模板再进行一点简单的补充

非类型模板参数

前面我们介绍的是类型模板参数,简单理解就是把类型当做参数传递给类/方法,今天我们第一个要介绍的就是与之对应的非类型模板参数

非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常

量来使用

比如说,我们想在c++中使用可以动态开辟大小的数组,就可以使用非类型模板参数

复制代码
namespace zzzyh
{
    // 定义一个模板类型的静态数组
    template<class T, size_t N = 10>
    class array
    { 
        public:
            T& operator[](size_t index){return _array[index];}
            const T& operator[](size_t index) const {return _array[index];}
            size_t size() const {return _size;}
            bool empty() const {return 0 == _size;}
        private:
        T _array[N];
        size_t _size;
    };
}

如上我们就可以模拟实现一个动态开辟的数组

注意,浮点数、类对象以及字符串是不允许作为非类型模板参数的,要求在编译时期确认参数大小

模板特化

模板的作用是忽略类型上的不同,抽取共性,复用同一套逻辑,那么我们现在需要实现一个比较大小的模板,看看会不会有什么意想不到的问题

复制代码
template<class T>
bool Less(T left, T right)
{
    return left < right;
}

看起来没什么问题,但是如果我们想比较两个地址里的值,用这种模板是有可能是不正确的

复制代码
int main()
{
	int b = 20;
	int a = 10;
	int* t2 = &b;
	int* t1 = &a;
	cout<<Less(t1, t2);
	return 0;
}

原因很简单,这里比较的是两个地址的大小

此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方

式。模板特化中分为函数模板特化与类模板特化

函数模板特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板

  2. 关键字template后面接一对空的尖括号<>

  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型

  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇

怪的错误

复制代码
template<>
bool Less<int*>(int* left, int* right)
{
	return *left < *right;
}

此时程序会自动调用特化之后的版本,而不走模板生成了

一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该

函数直接给出,不走模板特化的逻辑,比较推荐这种做法

类模板特化

全特化

全特化即是将模板参数列表中所有的参数都确定化

复制代码
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<<"Data<int, char>" <<endl;}
    private:
        int _d1;
        char _d2;
};

void TestVector()
{
    Data<int, int> d1;
    Data<int, char> d2;
}

偏特化

偏特化是与全特化相对的概念,其特点是特化部分参数,其中细分又可以分为指定具体的类型或者是像T*/T&这样的类型

部分特化

将模板参数类表中的一部分参数特化

复制代码
template <class T1>
class Data<T1, int>
{ 
    public:
        Data() {cout<<"Data<T1, int>" <<endl;}
    private:
    T1 _d1;
    int _d2;
};
特定类型
复制代码
//两个参数偏特化为指针类型
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;
};

模板分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有

目标文件链接起来形成单一的可执行文件的过程称为分离编译模式

我们推荐将.cpp文件和.h文件分离开来,因为这会使工程项目一目了然

但是模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义,,在连接时会报错

为了解决这个问题,我们一般将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种。

当然也可以在模板定义的位置显式实例化,不常用这种方法

结语

以上便是今天的全部内容。如果有帮助到你,请给我一个免费的赞。

因为这对我很重要。

编程世界的小比特,希望与大家一起无限进步。

感谢阅读!

相关推荐
cwtlw2 分钟前
java基础知识面试题总结
java·开发语言·学习·面试
西元.8 分钟前
多线程循环打印
java·开发语言·jvm
高林雨露8 分钟前
Kotlin 基础语法解析
android·开发语言·kotlin
ml1301852887415 分钟前
DeepSeek 助力心理医生小程序赋能!心理咨询小程序 线上咨询平台搭建
java·开发语言·小程序
不辉放弃16 分钟前
零基础讲解pandas
开发语言·python
tangweiguo0305198744 分钟前
(Kotlin)Android 高效底部导航方案:基于预定义 Menu 和 ViewPager2 的 Fragment 动态绑定实现
android·开发语言·kotlin
444A4E1 小时前
C++模板:泛型编程的魔法手册,从入门到“魔改”
c++·编译原理
ChiaWei Lee1 小时前
【C语言】深入理解指针(三):C语言中的高级指针应用
c语言·开发语言
最后一个bug1 小时前
教你快速理解linux中的NUMA节点探测是干什么用的?
linux·c语言·开发语言·arm开发·嵌入式硬件
Chiyamin1 小时前
C++面向对象速览(三)
c++