【初阶与进阶C++详解】第十二篇:模板进阶(函数模板特化+类模板特化+模板分离编译)

本文详细介绍了C++模板的使用,包括非类型模板参数、函数模板特化、类模板特化(全特化与偏特化)以及模板的分离编译问题。通过实例解析了模板的各个概念,并探讨了模板的优缺点,对于C++初学者和进阶者都是宝贵的学习资料。
摘要由CSDN通过智能技术生成

🏆个人主页企鹅不叫的博客

​ 🌈专栏

⭐️ 博主码云gitee链接:代码仓库地址

⚡若有帮助可以【关注+点赞+收藏】,大家一起进步!

💙系列文章💙

【初阶与进阶C++详解】第一篇:C++入门知识必备

【初阶与进阶C++详解】第二篇:C&&C++互相调用(创建静态库)并保护加密源文件

【初阶与进阶C++详解】第三篇:类和对象上(类和this指针)

【初阶与进阶C++详解】第四篇:类和对象中(类的六个默认成员函数)

【初阶与进阶C++详解】第五篇:类和对象下(构造+static+友元+内部类

【初阶与进阶C++详解】第六篇:C&C++内存管理(动态内存分布+内存管理+new&delete)

【初阶与进阶C++详解】第七篇:模板初阶(泛型编程+函数模板+类模板+模板特化+模板分离编译)

【初阶与进阶C++详解】第八篇:string类(标准库string类+string类模拟实现)

【初阶与进阶C++详解】第九篇:vector

【初阶与进阶C++详解】第十篇:list

【初阶与进阶C++详解】第十一篇:stack+queue+priority_queue+deque



💎一、非类型模板参数

模板参数分类类型形参非类型形参

  • 类型模板形参:出现在模板参数列表中,跟在class或者typename后面的参数类型名称。

  • 非类型模板形参:用一个常量(浮点数,字符串,类对象不允许作为非类型模板参数)(可以是 int short char long longlong)作为模板的一个参数,必须是整形家族中的类型参数。

  • template<class T =int, size_t N = 10>
    class array
    {
        private:
        T _array[N];
        size_t _size;
    }
    
    

💎二、模板的特化

模板特化:在原模板类的基础上,针对特殊类型所进行的特殊化的实现。分为函数模板特化类模板特化

🏆1.函数模板的特化

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

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

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

  4. 函数形参列表:必须和函数模板的基础参数类型完全一致

    // 模板
    template<class T>
    bool IsEqual(T& left, T& right)
    {
    	return left == right;
    }
    
     // 特化
    template<>
    bool IsEqual<const char* const>(const char* const& left, const char* const& right)
    {
    	return strcmp(left, right) == 0;
    }
    
    

🏆2.类模板的特化

2.1全特化

对类模板参数列表的类型全部都确定

template <class T1, class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<T1, T2>" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};

// 全特化
template<>
class Date<int, double>
{
public:
	Date()
	{
		cout << "Date<int, double>" << endl;
	}
private:
	int _d1;
	double _d2;
};

2.2偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本

偏特化有两种表现方式,一种是部分参数特化,一种是参数修饰特化

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

//第二个参数特化
template<class T1>
class test<T1, double>
{
public:
	test()
	{
		cout << "test<T1, double>" << endl;
	}

private:
	T1 _x;
	double _y;
};
int main()
{
	test<double, double> t1;
	test<int, double> t2;
}

参数更进一步限制,偏特化不指是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本

//如下有T1*和T2*,是模板的类型转为指针类型和引用类型
template<class T1, class T2>
class test<T1*, T2*>
{
public:
	test()
	{
		cout << "test<T1*, T2*>" << endl;
	}

private:
	T1* _x;
	T2* _y;
};

int main()
{
	test<int*, double*> t;
}

💎三、模板分离编译

🏆1.实例

**分离编译:**C++的编译器却不支持模板的分离编译,一旦进行分离编译,就会出现链接错误,下面代码就会报错。详情看这里传送门

// a.h
#pragma once


// 普通函数
void Swap(int& a, int& b);
// 函数模板
template<class T>
T Add(const T& a, const T& b);

// a.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "a.h"

// 普通函数
void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
// 函数模板
template<class T>
T Add(const T& a, const T& b)
{
	return a + b;
}

// test.cpp
#include "a.h"

int main()
{
	int a = 3;
	int b = 4;

	Swap(a, b);
	cout << "a = " << a << " b = " << b << endl;
	cout << Add(a, b) << endl;
	
	return 0;
}

🏆2.原因

1.模板在.cpp中定义了,由于不知道T的类型,所以没有对模板进行实例化。
2.a.h 和 a.cpp 都没有对模板进行实例化,因为不知道T的类型。
3.因为没有对模板进行实例化,所以没有函数参数,也就没有函数地址,所以在链接时,test.cpp中的调用Add函数时,没有函数地址,call调用不到Add函数,所以报错。

🏆3.解决方法

  1. 暴力:将声明和定义统一放在一个.h或.hpp的文件中
  2. 模板定义位置显示实例化(不推荐,这样就失去了泛型的特点)

💎四、模板优缺点

【优点】

​ 1.模板复用了代码,节省资源,更快的迭代开发

​ 2.增强了代码的灵活性

【缺点】

​ 1.导致编译时间变长

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


  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

penguin_bark

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值