C++ 模板进阶

news/发布时间2024/5/15 3:02:38

C++ 模板进阶

  • 一.非类型模板参数
    • 1.概念
    • 2.实例
    • 3.注意事项
  • 二.模板的特化
    • 1.引出
    • 2.函数模板的特化
      • 1.语法和使用
      • 2.建议
    • 3.类模板的特化
      • 1.全特化
      • 2.偏特化
        • 1.部分特化
        • 2.对参数进行进一步的限制
    • 4.匹配顺序
  • 三.模板的分离编译
    • 1.什么是分离编译
    • 2.模板的分离编译
    • 3.解决方法
      • 1.显式实例化(不推荐)
      • 2.分离编译放在头文件当中
  • 四.模板的总结

一.非类型模板参数

1.概念

模板参数分为: 类型形参与非类型形参

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

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

2.实例

比方说我想实现一个静态的栈,栈的大小是10

#define N 10template<class T>
class Stack
{
private:T _arr[N];
};int main()
{Stack<int> st1;Stack<string> st2;return 0;
}

在这里插入图片描述
可是我需求变了,st1我想要10个大小,st2我想要200个大小,st3我想要1000个大小,st4我想要2个大小…
怎么办?

此时就需要用到非类型模板参数了

template<class T,size_t N>
class Stack
{
private:T _arr[N];
};int main()
{Stack<int,10> st1;Stack<string,100> st2;Stack<char, 1000> st3;Stack<int*, 2> st4;return 0;
}

在这里插入图片描述
此时你st1想要10个大小,st2想要100个大小,st3想要1000个…
都可以,完美的解决了这个问题

3.注意事项

  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的
  2. 非类型的模板参数必须在编译期就能确认结果
    (因为模板是在编译阶段就要实例化的,而模板必须要知道实例化成的具体类型之后才能实例化,
    因此非类型模板参数和类型模板参数一样,必须在编译阶段就能够确认实例化成的具体类型才可以,
    否则编译阶段无法完成实例化,链接阶段就找不到实例化出的具体的类,进而发生链接错误)

二.模板的特化

1.引出

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

我们写一个非常简易的日期类,用这个日期类开始下面的介绍
这里给出了头文件,至于源文件就不给大家了,毕竟我们这篇博客的目的也不是实现日期类
关于日期类的完善大家可以看我的这篇博客:
C++类和对象中:运算符重载+const成员函数+日期类的完善
在这里插入图片描述
在这里插入图片描述
对于第三个比较,我们想要比较的其实是p1和p2,可是实际比较的是p1和p2这两个指针
不符合我们的期望,怎么办呢?

2.函数模板的特化

1.语法和使用

在这里插入图片描述
因此我们就可以这样特化

// 函数模板 -- 参数匹配
//基础的函数模板
template<class T>
bool Less(T left, T right)
{return left < right;
}
//函数模板的特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}

在这里插入图片描述
特化之后,如果函数模板的参数跟特化的类型是匹配的,那么就会走特化,并不会走函数的基础模板

跟我们之前学的函数模板推演实例化时如果有更匹配的会优先去匹配那个更匹配的,这个原则是一样的

2.建议

不建议大家使用函数模板的特化,而建议大家直接写一个同名的非函数模板即可,

因为:
1.函数模板的特化和直接写一个同名的非模板函数的工作量是一样的
2.函数模板的特化的要求更多
比如:必须要有一个基础的函数模板
而且函数形参表: 必须要和模板函数的基础参数类型完全相同
3.而且因为const,引用和指针的关系,导致函数模板的特化变得更加复杂
4.直接写一个同名的非函数模板:简单明了,代码的可读性高,容易书写
在这里插入图片描述

3.类模板的特化

1.全特化

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

语法跟函数模板的特化非常相似
都是需要一个基础模板等等…

template<class T1,class T2>
class C
{
public:C(){cout << "C<T1,T2>" << endl;}
};template<>
class C<int,double>
{
public:C(){cout << "C<int,double>" << endl;}
};

在这里插入图片描述

2.偏特化

偏特化:任何针对模版参数进一步进行条件限制而设计的特化版本
偏特化有两种表现形式:
部分特化和对参数进行进一步限制

偏特化也要求必须要有基础模板

1.部分特化

比如说特化第一个参数是int

template<class T1,class T2>
class C
{
public:C(){cout << "C<T1,T2>" << endl;}
};template<>
class C<int, double>
{
public:C(){cout << "C<int,double>" << endl;}
};template<class T>
class C<int,T>
{
public:C(){cout << "C<int,T>" << endl;}
};int main()
{C<int, double> c1;C<int, char> c2;C<char, int> c3;return 0;
}

在这里插入图片描述

2.对参数进行进一步的限制

偏特化并不仅仅是指特化部分参数,也可以用来对参数进行进一步的限制

针对于我们一开始引入的比较Date*的例子,我们可以使用类模板来解决这个问题

template<class T>
class D
{
public:bool less(const T& a, const T& b){return a < b;}
};//偏特化一个专门用于指针比较的类
template<class T>
class D<T*>
{
public:bool less(const T* a,const T* b){return *a < *b;}
};
int main()
{cout << D<int>().less(2, 1) << endl; // 可以比较,结果正确Date d1(2024, 8, 5);Date d2(2024, 8, 10);cout << D<Date>().less(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << D<Date*>().less(p1, p2) << endl; // 可以比较,结果正确return 0;
}

在这里插入图片描述
在这里插入图片描述
注意:这里不建议在偏特化指针类型时加上&

bool less(const T* const& a,const T*& b)

因为如果加上了&,就容易出现这种错误
在这里插入图片描述
此时只需要在引用前面加上const即可

bool less(const T* const& a,const T* const& b)

在这里插入图片描述

4.匹配顺序

模板参数的匹配原则:
会优先匹配更匹配的

如果跟全特化匹配,就匹配全特化
否则如果跟偏特化更匹配,就匹配偏特化
如果跟偏特化也不匹配,就匹配原模板

三.模板的分离编译

1.什么是分离编译

在这里插入图片描述

2.模板的分离编译

我们知道一般的类或者函数都是可以分离编译的,
短小,简单的函数定义为内联函数放在头文件当中
其他函数仅仅将声明放在头文件,定义放在源文件当中

对于带有模板的类或者函数,支持分离编译吗?

下面我们以函数模板为例,演示一下:
例如下面这份程序,对非模板函数f1和模板函数f2进行了分离编译
在这里插入图片描述
一编译,结果发现连接错误,原因是找不到模板函数f2的地址
在这里插入图片描述
为什么会这样呢?
下面我们来分析一下
首先,经过之前的学习,我们知道:
在这里插入图片描述
今天我们需要留意的是:
在这里插入图片描述
因此我们可以得出如下结论:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.解决方法

1.显式实例化(不推荐)

在定义该函数的源文件当中进行显式实例化

func.cpp
//显式实例化
template
void f2<int>(const int& a);//f2的定义
template<class T>
void f2(const T& a)
{cout << "f2<T>" << endl;
}

在这里插入图片描述
不过这种方法并不好用,甚至非常难用
比如,我现在不想f2(1)这么调用了
我想f2(1.1)这么调用,让T实例化为double
此时又链接错误了…
在这里插入图片描述

2.分离编译放在头文件当中

那么怎么办呢?
要不然干脆不进行声明跟定义分离,这样当然可以
要不然声明跟定义分离,但是将定义也放在头文件当中
这样的话,用声明的地方一定有定义
编译阶段就能够正常实例化出模板参数的类型了

通常情况下,这种包含模板的头文件习惯性命名为.hpp文件
当然命名为.h文件也是可以的
在这里插入图片描述
这种时候你想怎么玩就怎么玩

四.模板的总结

在这里插入图片描述

以上就是C++ 模板进阶的全部内容,希望能对大家有所帮助!

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

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

相关文章

2024.2.15每日一题

LeetCode 二叉树的层序遍历 II 107. 二叉树的层序遍历 II - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&am…

【计算机网络】网络编程套接字UDP服务器客户端的简单模拟

需要云服务器等云产品来学习Linux的同学可以移步/–>腾讯云<–/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;优惠多多。&#xff08;联系我有折扣哦&#xff09; 文章目录 1. 前置知识1.1 源IP和目的IP1.2 端口号1.3 TCP协议和UDP协议初识1.4 网络字节序1.5 …

Linux中信号机制

信号机制 信号的概念 概念&#xff1a;信号是在软件层次上对中断机制的一种模拟&#xff0c;是一种异步通信方式 所有信号的产生及处理全部都是由内核完成的信号的产生&#xff1a; 1 按键产生 2 系统调用函数产生&#xff08;比如raise&#xff0c; kill&#xff09; 3 硬件…

Kubernetes(K8s)的基础概念

K8s的概念 K8S 的全称为 Kubernetes (K12345678S) &#xff08;简化全称&#xff09; Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于 管理容器化工作负载和服务&#xff0c;有助于声明式配置和自动化。它拥有庞大且快速发展的生态系统。Kubernetes 服务、支持和…

Vue中$root的使用方法

查看本专栏目录 关于作者 还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#x…

【Java】零基础蓝桥杯算法学习——线性动态规划(一维dp)

线性dp——一维动态规划 1、考虑最后一步可以由哪些状态得到&#xff0c;推出转移方程 2、考虑当前状态与哪些参数有关系&#xff0c;定义几维数组来表示当前状态 3、计算时间复杂度&#xff0c;判断是否需要进行优化。 一维动态规划例题&#xff1a;最大上升子序列问题 Java参…

Java基于微信小程序的电子竞技信息交流平台

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

07-k8s中secret资源02-玩转secret

一、回顾secret资源的简单实用 第一步&#xff1a;将想要的数据信息【key&#xff1a;value】中的value值&#xff0c;使用base64编码后&#xff0c;写入secret资源清单中&#xff1b; 第二步&#xff1a;创建secret资源&#xff1b; 第三步&#xff1a;pod资源引用secret资源&…

相机图像质量研究(12)常见问题总结:光学结构对成像的影响--炫光

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

代码随想录Leetcode70. 爬楼梯

题目&#xff1a; 代码&#xff08;首刷自解 2024年2月19日&#xff09;&#xff1a; 空间复杂度为O(N)&#xff0c;如果想要优化空间复杂度&#xff0c;则只用三个变量进行状态转移也可以&#xff0c;参考 代码随想录 Leetcode509. 斐波那契数-CSDN博客 class Solution { pu…

xilinx除法器的使用

平台&#xff1a;Vivado2018.3. 芯片&#xff1a;xcku115-flva1517-2-i (active) 最近学习使用了xilinx除法器&#xff0c;在使用过程中出现了很多次除法器的结果和我预计的结果不一致&#xff0c;特此记录学习一下。 参考文件&#xff1a;pg151.下载地址 pg151-div-gen.pdf …

如何使用静态住宅ip代理进行Facebook群控?

在进行Facebook群控时&#xff0c;ip地址的管理是非常重要的&#xff0c;因为Facebook通常会检测ip地址的使用情况&#xff0c;如果发现有异常的使用行为&#xff0c;比如从同一个ip地址频繁进行登录、发布内容或者在短时间内进行大量的活动等等&#xff0c;就会视为垃圾邮件或…

C++——日期类

前言&#xff1a;哈喽小伙伴们&#xff0c;在上一篇文章中我们对C类与对象的前半段知识进行了简单的分享&#xff0c;其中比较重要的莫过于C类的六个默认成员函数。 所以这篇文章&#xff0c;我们通过实现一个完整的日期的操作&#xff0c;来对这些成员函数有一个更加深入的理…

Linux学习笔记(centOS)—— 文件系统

目录 一、Linux中的文件 打开方式 二、目录结构​ 三、相关命令 切换目录命令 列出当前目录下的文件和目录命令 一、Linux中的文件 “万物皆文件。” 图1.1 所有文件 打开方式 图形化界面左上角的位置→计算机&#xff0c;打开以后就可以看到Linux全部的文件了&#xf…

vue3实现瀑布流布局组件

先看效果图 直接上代码 utils.js // 用于模拟接口请求 export const getRemoteData (data 获取数据, time 2000) > {return new Promise((resolve) > {setTimeout(() > {console.log(模拟获取接口数据, data)resolve(data)}, time)}) }// 获取数组随机项 export…

08MARL深度强化学习 independent learning

文章目录 前言1、Independent Value-based Learning2、Independent Policy Gradient Methods 前言 记录independent learning算法的基础概念&#xff0c;使用一些RL算法训练多智能体 1、Independent Value-based Learning 基于值的独立学习算法&#xff1a;每个智能体根据自身…

jsp课程教学管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 课程教学管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0…

第13章 网络 Page741~744 asio核心类 ip::tcp::socket

1. ip::tcp::socket liburl库使用"curl*" 代表socket 句柄 asio库使用ip::tcp::socket类代表TCP协议下的socket对象。 将“句柄”换成“对象”,因为asio库是不打折扣的C库 ip::tcp::socket提供一下常用异步操作都以async开头 表13-3 tcp::socket提供的异步操作 …

docker本地目录挂载

小命令 1、查看容器详情 docker inspect 容器名称 还是以nginx为例&#xff0c;上篇文章我们制作了nginx静态目录的数据卷&#xff0c;此时查看nginx容器时会展示出来&#xff08;docker inspect nginx 展示信息太多&#xff0c;这里只截图数据卷挂载信息&#xff09;&#…

FOC电流环速度环调试记录

FOC电流环速度环调试记录 电流环&#xff1a; 首先foc控制中都采用PI控制&#xff0c;没有引入微分&#xff0c;因为电流的采样率非常高不需要加入微分项&#xff1b;微分项的加入&#xff0c;会使电流采样中的高频小信号误差起到放大的作用&#xff0c;把小的误差放大&#…
推荐文章