当前位置: 首页 > news >正文

【C++】之模板进阶

目录

一.非类型模板参数

1.概念

2.使用

3.常见错误

二.模板的特化

1.什么是模板的特化

函数模板特化

类模板特化

2.全特化和偏特化

全特化

偏特化(半特化)

三.模板的分离问题


一.非类型模板参数

1.概念

本质: 用一个常量做为类(函数)模板的参数, 同时可以把它当作常量来使用

注: 非类型模板参数只能是整型家族(size_t/ int/ char)

template<class T1, size_t N>
void test1(T1 t)
{
	cout << "t: " << t << endl;
	cout << "size_t N: " << N << endl;
}
template<class T2, size_t N = 20>
void test2(T2 t)
{
	cout << "t: " << t << endl;
	cout << "size_t N: " << N << endl;
}
//模板进阶

int main()
{
	//一.非类型模板参数
	int a = 20;
	//1.显式传入N的大小(注意只能传入常量, 并不能传入变量)
	test1<int, 10>(a);
	//2.可以给N缺省值
	test2(a);

	return 0;
}

2.使用

非类型模板参数的具体使用场景

在学习非类型模板参数之前我们只能用#define N 5或者 const N = 5这样的方式来定义宏N, 这个N在每次实例化的时候值不会改变, 但是引入非类型模板参数之后, 我们每次实例化的时候, 都可以显示去传一个非类型模板参数, 从而每次控制N的大小, 这个N如果不需要改变的话我们也没, 必要每次都显式的传, 此时我们就可以给N一个缺省值

template<class T3, size_t N = 5>
class my_test 
{
public:
	void print()
	{
		cout << "_array_size: " << N << endl;
	}
private:
	T3 _array[N];//这个数组每次实例化模板类可以不定长度
};

int main()
{
	my_test<int, 20> myt1;
	myt1.print();
	my_test<int> myt2;
	myt2.print();

	return 0;
}

3.常见错误

1).

 2).

3).

二.模板的特化

1.什么是模板的特化

本质: 对于特殊的模板类型做出特殊的处理, 这个特殊处理是你自己指定的

函数模板特化

对于int*做出了特殊处理, 当模板类型推导出的是int*时, 单独走int*的函数逻辑

为什么要这么做?

如果我现在要比一对数据, 直接比他们的值是没有问题的, 但是如果我给出这两块待比较数据的指针呢? 指针可不能直接用大于/小于来比较, 需要先解引用再进行比较, 所以以下拿int*来做为例子

这时会有人问, 那么每次保证传入的都是数据的值不就好了吗?我在传入的时候, 就传入*pa/*pb, 这样不就可以直接比较了?

这显然不是一种好的解决方式, 比如priority_queue用的是仿函数less/greater来比较的, 比如现在有一个日期类, 我可以在优先级队列存data类型的数据, priority_queue<data>, 那如果我要存入的数据是需要new出来的, 我就要在优先级队列存入data*类型的数据, priority_queue<data*>, 这时我们想要比的是data的数据, 而并非data的地址, 我们就要用模板的特化来处理这类问题, 给less/greater一个针对于T*的独特处理方式(这属于偏特化)

template<class T>
bool my_less(T l, T r)
{
	return l < r;
}

//模板的特化
template<>
bool my_less<int*>(int* l, int* r)
{
	return *l < *r;
}

//----------------------------------------

int a = 10;
int b = 20;

if (my_less(a, b))
{
	cout << "T: int -- a < b " << endl;
}
else
{
	cout << "T: int -- a > b " << endl;
}

int* pa = &a;
int* pb = &b;

if (my_less(pa, pb))
{
	cout << "T: int* -- a < b " << endl;
}
else
{
	cout << "T: int* -- a > b " << endl;
}

类模板特化

template<class K1>
class A
{
public:
	void print()
	{
		cout << "A<K1>" << endl;
	}
private:
	K1 a;
};

template<>
class A<char>
{
public:
	void print()
	{
		cout << "A<char>" << endl;
	}
private:
	char b;//可以省略
};

A<char> a;
a.print();
A<int> b;
b.print();

  

2.全特化和偏特化

全特化

全特化就是将所有的模板参数全部特化, 上面的例子就是一个偏特化, 因为他只有一个模板参数, 以下我们再举例一个

给出两个模板参数T1和T2, 全部特化为int, double

必须要两个参数与int, double严格匹配, 才可以调用到全特化的模板函数

//函数模板全特化
template<class T1, class T2>
void my_test1(T1 a, T2 b)
{
	cout << "调用模板函数my_test1" << endl;
}
//全特化
template<>
void my_test1<int, double>(int a, double b)
{
	cout << "调用全特化模板函数my_test1" << endl;
}
//可以写成void my_test1(int a, double b)但这就不是模板的特化,而是定义了函数

my_test1(10, 20);//(int, int)
my_test1(10.10, 20.20);//(double, double)
my_test1('a', 20.20);//(char, double)
my_test1(10, 20.20);//(int, double)

//类模板全特化
template<class K3, class K4>
class B
{
public:
	void print()
	{
		cout << "A<K3, K4>" << endl;
	}
private:
	K3 a;
	K4 b;
};

template<>
class B<char, int>
{
public:
	void print()
	{
		cout << "A<char, int>" << endl;
	}
private:
	char a;//可以省略
	int b;//可以省略
};

B<int, int> c;
c.print();
B<char, int> d;
d.print();

偏特化(半特化)

偏特化可以理解为将部分模板参数特化, 但不止于此

注: 函数模板不支持偏特化, 但是可以写为函数模板重载

//函数模板不支持偏特化, 但是可以写为函数模板重载
template<class M1, class M2>
void MyTest(M1 a, M2 b)
{
	cout << "<M1, M2>" << endl;
}
template<class M1>
void MyTest(M1 a, char b)
{
	cout << "<M1, char>" << endl;
}
template<class M1, class M2>
void MyTest(M1* a, M2* b)
{
	cout << "<M1*, M2*>" << endl;
}

int a = 10;
int b = 20;
MyTest(10, 20);
MyTest(10, 'a');
MyTest(&a, &b);

//类模板偏特化
template<class K5, class K6>
class C
{
public:
	void print()
	{
		cout << "<K5, K6>" << endl;
	}
};
//偏特化(部分模板参数特化)
template<class K6>
class C<double, K6>
{
public:
	void print()
	{
		cout << "<double, K6>" << endl;
	}
};
//偏特化(对于推导的K5K6的类型, 特定形式发生特化)
template<class K5, class K6>
class C<K5*, K6*>
{
public:
	void print()
	{
		cout << "<K5*, K6*>" << endl;
	}
};


C<int, int>().print();
C<double, char>().print();
C<double, double>().print();
C<double*, double>().print();
C<int*, int*>().print();
C<double*, double*>().print();

总结: 函数模板只能全特化, 类模板可以全特化/偏特化, 函数模板可以用重载的方式来代替偏特化

三.模板的分离问题

模板不可以分文件编写, 一但分文件编写, 就会报出链接错误

为什么会有链接错误?

因为对于模板而言, 只是一种定义规范, 并没有真正的定义, 而是在实例化的时候, 才会去真正的定义, 所以如果分文件编写, 在包含了头文件之后, 就只有声明而并不能找到实现, 因为模板是在实例化的时候才去定义, 所以模板定义的函数不会写入符号表, 也就不会被声明找到, 从而引发链接错误

模板定义的函数实例化后依旧不会写入符号表

模板必须把定义和声明写在一个文件(在文件内可以声明与定义分离), 所以不存在需要链接的场景, 每次使用这个模板时包含头文件, 每次包含头文件就同时包含了声明与定义, 所有模板实例化后定义的函数就没有必要写入符号表, 由于在同一文件, 故在编译阶段就已经确定地址了!

...

有一种方式可以让模板分文件编写, 但不推荐!!这里只是知道一下而已, 正常来说不会这么写

实现了模板分文件编写后, 在模板的定义的那个文件最后显式实例化

拿vector类模板举例

//vector的push_back, pop_back...的定义
//......
//......

//显式实例化
//用到哪个实例化模板类就显式写哪个
//比如我要用到vector<int>和vector<double>
//以下我就需要写
template
vector<int>

template
vector<double>

相关文章:

  • 100天精通Python(数据分析篇)——第58天:Pandas读写数据库(read_sql、to_sql)
  • Bean的生命周期
  • 哈希桶(详解创建)
  • 回归预测 | MATLAB实现SSA-BP多输入单输出回归预测
  • 【雅思备考】听说读写攻略 | 雅思核心词汇之科技类
  • Python-列表,从基础到进阶用法大总结,进来查漏补缺
  • JDBC模拟SQL注入和避免SQL注入
  • flink在企业IT架构中如何定位-在选型流批一体技术与大数据架构时的避坑指南
  • JUC并发编程之CompletableFuture基础用法
  • SpringBoot+Mybatis-Plus多数据源使用
  • Colab-免费GPU算力
  • 【CH559L单片机】串口下载程序说明
  • CMake中macro的使用
  • windows利用msys2安装minGW64
  • (42)STM32——LCD显示屏实验笔记
  • 分享一款快速APP功能测试工具
  • Docker 笔记(2):Dockerfile
  • egg(89)--egg之redis的发布和订阅
  • flask接收请求并推入栈
  • laravel 用artisan创建自己的模板
  • leetcode98. Validate Binary Search Tree
  • Linux Process Manage
  • linux学习笔记
  • Python 反序列化安全问题(二)
  • React 快速上手 - 07 前端路由 react-router
  • Spring-boot 启动时碰到的错误
  • XForms - 更强大的Form
  • 百度小程序遇到的问题
  • 记一次和乔布斯合作最难忘的经历
  • 记一次用 NodeJs 实现模拟登录的思路
  • 开源SQL-on-Hadoop系统一览
  • 力扣(LeetCode)357
  • 突破自己的技术思维
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • 数据库巡检项
  • ​渐进式Web应用PWA的未来
  • # Maven错误Error executing Maven
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (10)ATF MMU转换表
  • (4)(4.6) Triducer
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (ZT)一个美国文科博士的YardLife
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (区间dp) (经典例题) 石子合并
  • (新)网络工程师考点串讲与真题详解
  • (译) 函数式 JS #1:简介
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • .NET DataGridView数据绑定说明
  • .NET高级面试指南专题十一【 设计模式介绍,为什么要用设计模式】
  • .NET轻量级ORM组件Dapper葵花宝典
  • .net网站发布-允许更新此预编译站点
  • .NET项目中存在多个web.config文件时的加载顺序
  • .NET中使用Protobuffer 实现序列化和反序列化
  • /var/log/cvslog 太大
  • @ModelAttribute注解使用