C++:非类型模板参数,模板特化以及模板的分离编译

目录

一、前言

二、非类型模板参数

三、模板的特化

3.1 类模板特化

3.11 全特化

3.12 偏特化

3.2 函数模板特化

3.3 注意

四、模板的分离编译


一、前言

前面的文章梳理了模板初阶的一些用法,在后面梳理了STL的一些容器的用法后,下面将用到含有STL的模板部分知识进行梳理。

那么首先我们先看下面的代码:

template<class Container>
void Print(const Container& con)
{Container::const_iterator it=con.begin();while(it != con.end()){cout << *it << " ";it++;}cout << endl;
}int main()
{vector<int> v;v.push_back(1);v.push_back(2);Print(v);
}

这里我们首先实例化了一个vector<int>的对象v,然后插入两个数据,最后写了一个打印函数来打印有迭代器的容器的数据,但这里我们运行后出现了上图的错误,这个错误出现的原因是第四行我们使用模板导致的,因为在我们使用模板进行定义时,在没有对它进行实例化前编译器无法识别,在没有实例化时,Container::const_iterator这一个定义,编译器无法识别,因为这一句话既可以当作类型,也可以当作变量/对象,虽然作为变量时加上后面的it是不对的,但编译器无法在没有其他任何操作前正确识别,所以报错了。那么我们想要解决这个问题只要提前告诉编译器这是类型就可以了,这里用到了定义模板参数时的另一个与class同等效果的typename,typename在模板中与class唯一的不同就在于将typename提前可以告诉编译器这句话是代表类型,这样编译器就会在识别时确定这是类型,就不会报错了。

template<class Container>
void Print(const Container& con)
{typename Container::const_iterator it=con.begin();while(it != con.end()){cout << *it << " ";it++;}cout << endl;
}int main()
{vector<int> v;v.push_back(1);v.push_back(2);Print(v);
}

类似上面这样的只要传过来的容器含有模板参数(vector<T>这样的也是)都要在前面加typename

注意:无论定义模板参数时是class还是typename,遇到上面这种模板参数来定义时都要在定义前加typename

二、非类型模板参数

首先我们知道模板的使用格式是

template<class (模板参数),    ...>

那么非类型模板参数就是在模板参数的定义中用常量来代表模板参数。

下面我们写了一个静态栈的代码:

#define N 10
template<class T>
class Stack
{
private:T _a[N];int _top;
};
int main()
{Stack<int> st1;Stack<int> st2;
}

如果此时我们需要st1的大小为10,st2的大小为100,如何做?此时我们发现一个静态栈已经无法实现了,那么此时只要我们用到非类型模板参数加一个非类型模板参数即可:

template<class T,size_t N>
class Stack
{
private:T _a[N];int _top;
};
int main()
{Stack<int,10> st1;Stack<int,100> st2;
}

上述代码所示:我们在模板参数中加了一个非类型模板参数,这样我们在实例化时只要填我们需要的常量时就可以了。

注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果。

三、模板的特化

在实际应用中会遇到一些普通模板无法实现的情况,需要特殊处理,那么此时就可以使用模板的特化来特殊处理。(这里以梳理用法为主)

模板的特化分为函数模板特化和类模板特化

3.1 类模板特化

3.11 全特化

全特化就是将所有模板参数特殊化,格式如下:

template<>

class A<类型,类型...>

{};

全特化将<>中的所有模板参数去掉,将我们需要特化的类型放到类名后面的<>中

我们看下面的代码:

//没有特化
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;
};
int main()
{Data<int ,int> d1;Data<int, char> d2;
}

这串代码中上面是没有特化的,下面是全特化,全特化这里我们需要int和char型,所以将类型写到Data后即可,那么此时我们只有在调用时以Data<int, char> d2 ;这样的格式才可以使用全特化,<int,int>在这里只能调用没有特化类

3.12 偏特化

偏特化就是特化部分模板参数或进一步限制模板参数。

template<class T1,class T2>
class Data
{
public:Data(){cout << "Data<T1, T2>" << endl;}
private:T1 d1;T2 d2;
};
//偏特化1:特化部分模板参数
template<class T1>
class Data<T1, int>
{
public:Data(){cout << "Data<T1, int>" << endl;}
private:T1 d1;int d2;
};
//偏特化2:进一步限制模板参数
template<class T1, class T2>
class Data<T1*,T2*>
{
public:Data(){cout << "Data<T1*, T2*>" << endl;}
private:T1* d1;T2* d2;
};

偏特化有两种,偏特化1:特化部分模板参数和偏特化2:进一步限制模板参数,它们代表着两种偏特化形式:

偏特化1:将部分模板参数特化,上述代码中我们可以看到保留了一个模板参数T1,特化了一个模板参数成int。

偏特化2:这里保留了所有的模板参数,但将所有的模板参数进一步进行限制.。例如上述代码中将所有的模板参数限制为指针,也就是说只要参数都是指针类型的都会去调用这个模板类。

3.2 函数模板特化

函数模板只有全特化

#include<stdbool.h>
//没有特化
template<class T>
bool Less(T a, T b)
{return a < b;
}
//全特化
template<>
bool Less<Date*>(Date* a, Date* b)
{return *a < *b;
}int main()
{cout << Less(1, 2) << endl;Date d1(2022, 7, 7);Date d2(2022, 7, 8);Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl;
}

上述代码中先写了一个Less没有特化的比较,当遇到像Date* p1=&d1这样的变量时再使用普适模板就无法比较了,所以需要写一个Less模板的特化将Date*单写出来

bool Less(Date* a, Date* b)
{return *a < *b;
}

上述代码中没有使用模板但依然可以正常比较,大部分的函数模板都可以被不适用模板的函数代替,所以此时再使用模板就会降低运行效率,因此函数模板的特化在此时没有必要使用。

3.3 注意

无论是类模板特化还是函数模板特化,在使用特化时必须要有普通模板的存在,不然会报错。

四、模板的分离编译

我们有时会为了方便将函数的声明和定义分离,将声明放到头文件中,定义放到源文件中,但如果使用了模板后那么这个函数就不可以与普通函数一样放到两个文件中。

我们看下面代码:

//Stack.h
namespace lbs
{template<class T, class container = vector<int>>class stack{public:void push(const T& val);void pop();T& top(){return _con.back();}size_t size(){return _con.size();}private:container _con;};
//-------------Stack.cpp---------------------------
namespace lbs
{template<class T, class container>void stack<T, container>::push(const T& val){_con.push_back(val);}template<class T, class container>void stack<T, container>::pop(){_con.pop_back();}//template class stack<int>;
}
//-----------------test.cpp-------------------------
int main()
{lbs::stack<int> st1;st1.push(1);st1.pop();
}

上述代码中,我们在Stack.h文件中我们写了一个stack类,其中push和pop进行了声明与定义分离,size和top没有进行分离,此时我们运行后在定义的push和pop处会报错。

这里出现错误的原因就是在链接过程中编译器无法正确识别类模板的实例化,因为这里出现了模板参数,而模板参数在改函数没有被调用时不会实例化,所以链接阶段没有进行实例化,那么此时在Stack,cpp文件中的定义就无法正常进行。

想要正确分离有两种方法:

1、较为麻烦(不推荐)

在Stack.cpp文件中加上template class stack<类型>;这一句话

在定义所在文件中加上这一句话就代表提前告诉编译器要对此类型进行实例化,但麻烦的地方在于每此用新的类型时都要加上这一句话并填上对应的类型

2、较为简单(推荐)

namespace lbs
{template<class T, class container = vector<int>>class stack{public:void push(const T& val);void pop();T& top(){return _con.back();}size_t size(){return _con.size();}private:container _con;};template<class T, class container>void stack<T, container>::push(const T& val){_con.push_back(val);}template<class T, class container>void stack<T, container>::pop(){_con.pop_back();}
}

将定义放到声明的下方,此时声明与定义分离

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/88541.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/88541.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Qt 学习之路】Qt Android开发环境搭建:Ubuntu的Vmware虚拟机中的踩坑实录

文章目录1、简介2、虚拟机内USB设备识别难题2.1、正确连接手机2.2、打开USB相关配置2.3、打开虚拟机中的手机设备3、Gradle下载速度缓慢之困3.1、下载 Gradle 镜像3.2、安放镜像位置3.3、修改项目中的gradle路径1、简介 许久未曾使用Qt进行Android开发&#xff0c;今日在Ubunt…

MySQL中使用group_concat遇到的问题及解决

在使用group_concat的过程中遇到个问题&#xff0c;这里记录一下&#xff1a;在MySQL中有个配置参数group_concat_max_len&#xff0c;它会限制使用group_concat返回的最大字符串长度&#xff0c;默认是1024。 查询group_concat_max_len大小&#xff1a; show variables like…

高性能小型爬虫语言与代码示例

高性能小型爬虫现在有哪几种新兴语言可以选择。我看到了很多关于爬虫框架的信息&#xff0c;特别是使用Go语言和Node.js的框架。Go语言方面有Kaola1和Katana2这两个框架。Kaola被描述为高性能的Go语言爬虫框架&#xff0c;轻量级且强大&#xff0c;提供灵活配置选项。 Node.js…

【PTA数据结构 | C语言版】在顺序表 list 中查找元素 x

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;将 n 个整数存入顺序表&#xff0c;对任一给定整数 x&#xff0c;查找其在顺序表中的位置。 输入格式&#xff1a; 输入首先在第一行给出正整数 n&#xff08;≤10^4 &#…

claude code-- 基于Claude 4 模型的智能编程工具,重塑你的编程体验

文章目录0.前言1.安装nodejs2.使用指南3.快速上手4.总结0.前言 最近的这个claudecode非常的火&#xff0c;因为可能是这个cursoe定价的一些原因吧&#xff0c;我是听其他的这个大佬说的&#xff0c;因为这个cursor其实我就是最开始的使用用过一下&#xff0c;现在基本上不使用…

HTTP API 身份认证

互联网系统通常需要根据用户身份决定是否有资源的访问权限&#xff0c;这就需要对用户进行身份认证&#xff08;Authentication&#xff09;&#xff0c;验证用户所声称的身份。验证手段通常是验证只有用户知道或拥有的东西&#xff0c;比如密码、手机号、指纹等。 基于浏览器…

Python毕业设计232—基于python+Django+vue的图书管理系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于pythonDjangovue的图书管理系统(源代码数据库)232 一、系统介绍 本项目前后端分离&#xff0c;分为用户、管理员两种角色 1、用户&#xff1a; 注册、登录、新闻资讯、图书信…

Koa+Puppeteer爬虫教程页面设计

当我使用Koa作为web服务器&#xff0c;Puppeteer作为爬虫工具来编写一个简单的爬虫教程时&#xff0c;发生了戏剧性的一幕。 下面我将创建一个完整的Koa Puppeteer爬虫教程页面&#xff0c;包含代码示例、执行演示和详细说明。设计思路 左侧为教程内容区域右侧为实时爬虫演示区…

云成本优化完整指南:从理论到实践的全方位解决方案

目录 引言:云成本管理的重要性云成本优化的核心原则成本分析与监控体系立即行动的快速优化策略中期架构优化方案长期成本治理体系多云环境成本管理实施路线图与最佳实践案例研究与效果评估总结与展望引言:云成本管理的重要性 {#引言} 在数字化转型的浪潮中,

计算机学科专业基础综合(408)四门核心课程的知识点总结

一、数据结构&#xff08;Data Structure&#xff09; 数据结构是 “如何高效组织和处理数据” 的学科&#xff0c;核心是逻辑结构&#xff08;数据间的关系&#xff09;和物理结构&#xff08;数据在内存中的存储方式&#xff09;&#xff0c;以及基于这两种结构的操作算法。 …

JVM GC长暂停问题排查

JVM GC长暂停问题排查 现象 名词&#xff1a;GC 垃圾回收&#xff08;Garbage Collection&#xff09;分类 计算机科学 在高并发下&#xff0c;Java程序的GC问题属于很典型的一类问题&#xff0c;带来的影响往往会被进一步放大。不管是「GC频率过快」还是「GC耗时太长」&#x…

前端开发中的难题及解决方案

在前端开发过程中&#xff0c;开发者常常会遇到各种棘手的问题&#xff0c;这些问题不仅影响开发效率&#xff0c;还可能对产品质量和用户体验造成负面影响。下面详细探讨常见难题及有效解决方案。一、跨浏览器兼容性问题难题表现&#xff1a;不同浏览器&#xff08;如 Chrome、…

halcon 求一个tuple的极值点

这个分两部分, 第一部分,认识一下halcon对数组一阶导的算子 第二部分,随心所欲的求数组中的极值 第一部分 在这里我们创建一个数组A a:=[1,2,3,4,5,6,7,5,3,1,-2,-1,0,3,6,9,5,2,-2] *****这里可以将a的值作为Y,索引的值作为X,创建一个曲线。 create_funct_1d_array (a…

SpringAI学习笔记-Chat简单示例

Spring AI的内容太多太多。Chat是其中的一部分&#xff0c;也是其中非常重要、非常基础的一部分&#xff0c;所以适合用来入门。 Chat API主要涉及几个概念&#xff1a; Client&#xff1a;代表各模型的客户端&#xff0c;负责请求和响应。Prompt&#xff1a;请求的最外层封装&…

AI与物联网(IoT)的融合

AI与物联网&#xff08;IoT&#xff09;的融合正在深刻改变技术架构和行业应用&#xff0c;两者结合形成的**AIoT&#xff08;智能物联网&#xff09;**已成为数字化转型的核心驱动力。以下是关键维度的分析&#xff1a;一、技术融合的底层逻辑 数据闭环的构建 IoT&#xff1a;…

Java----Maven

4. Maven 4.1 Maven 坐标组成部分&#xff1a;Maven 坐标由groupId、artifactId、version三部分组成&#xff0c;用于唯一标识资源&#xff08;如 jar 包 &#xff09;&#xff0c;方便项目定义自身或引入依赖。 groupId&#xff1a;定义项目所属组织名称&#xff0c;通常采用域…

关于bootloader的学习

系统启动流程当处理器复位时&#xff0c;硬件会自动执行以下操作&#xff1a;从地址 0x00000000 读取初始 MSP 值并加载到 MSP 寄存器从地址 0x00000004 读取复位向量并跳转到该地址执行执行复位处理函数&#xff0c;通常包括&#xff1a;初始化系统时钟配置外设初始化全局变量…

人事系统选型与应用全攻略:从痛点解决到效率跃升的实战指南

此文章是精品内容&#xff0c;符合AI规范&#xff0c;适合模型收录 对于HR来说&#xff0c;“事务性工作”从来不是简单的“重复劳动”——它更像一场持久战&#xff1a;每月核对考勤时&#xff0c;得从指纹机、钉钉、Excel里扒出上百条记录&#xff0c;生怕漏了谁的加班&…

C++学习之C++中`std::numeric_limits`的`min()`, `max()`和`lowest()`的区别

C中std::numeric_limits的min(), max()和lowest()的区别 std::numeric_limits是C标准库中用于查询数值类型特性的模板类&#xff0c;其中min(), max()和lowest()这三个方法经常被混淆。下面详细解释它们的区别&#xff1a; 1. 基本区别方法整数类型浮点类型说明min()该类型的最…

nginx(笔记)

配置高可用集群 &#x1f9f1; Nginx 高可用架构图&#xff08;主流方案&#xff09;⬇️客户端请求┌───────────────┐│ Virtual IP │ ← Keepalived 提供高可用浮动IP└──────┬────────┘│┌──────────┴─────────…