C++模板进阶

C++教学总目录

C++模板进阶

  • 1、模板初阶的补充
  • 2、非类型模板参数
  • 3、模板的特化
    • 3.1、函数模板特化
    • 3.2、类模板特化
      • 3.2.1、全特化
      • 3.2.2、偏特化
      • 3.2.3、类模板特化的应用
  • 4、模板的分离编译

1、模板初阶的补充

现在假设我们有一个vector对象,我们要遍历输出vector对象中的所有数据,代码如下:

void Print(const vector<int>& v)
{vector<int>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;
}

但是接下来我还有一个list容器,我也要遍历输出list容器中的所有数据。并且之后可能还会有其他容器要遍历输出容器内的所有数据。基于这样的原因:我们可以直接把Print函数写成模板函数。

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

但是编译之后发现报错了:
在这里插入图片描述
这是因为Container::const_iterator可能是类型,也可能是一个对象,如果是类型就没有问题,但是如果是一个对象就存在语法错误。
说白了这里就是因为类中可能有静态成员变量,如果是静态成员变量就是一个对象,那么就不符合语法,如果是类型就没有问题。

基于上面的原因,我们需要在Container前面加上typename告诉编译器这是一个类型,等到对象实例化的时候到类中去找const_iterator这个类型。
如下:对于vector对象和list对象都可以调用

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

在这里插入图片描述
在优先级队列这里less的数据类型传的是Container::value_type,由于无法判断是对象还是类型,所以也需要在前面加上typename, 明确告诉编译器这里是类型。


2、非类型模板参数

在过去,我们实现一个栈的方式如下:

#define N 100
// 静态栈
template<class T>
class Stack
{
private:T _a[N];int _top;
};

有了非类型模板参数,我们可以这么写:

template<class T, size_t N>
class Stack
{
private:T _a[N];int _top;
};

有了非类型模板参数N,需要多少容量我们可以自己传递。
但是需要注意两个点:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果。


库里实现的有bitset(位图)后面我们会学习。这里讲一个array:
在这里插入图片描述
array是C++11的产物,包含于头文件<array>,类似数组,是一个固定长度的容器。需要穿类型和容器的容量N。
实际上array和数组差不多,只不过对于越界的访问更加严格。
array重载operator[]中对pos位置进行断言检查,而普通数组越界编译器可能检查不出来。

用法如下:

array<int, 10> a;
a[0] = 0;
for (auto e : a)
{cout << e << " ";
}
cout << endl;

不过基本上没啥用,很鸡肋。


3、模板的特化

3.1、函数模板特化

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理。

实现一个模板函数,如果x1 < x2返回true。

template<class T>
bool Less(T x, T y)
{return x < y;
}int main()
{int a = 2, b = 1;int* p1 = &a;int* p2 = &b;cout << Less(a, b) << endl;cout << Less(p1, p2) << endl;return 0;
}

上面比较两个整形是没有问题的。但是如果比较的是两个指针呢?这样就变成了比较两个指针地址的大小了,是不符合我们需求的。这时候我们就需要使用模板的特化。

函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。

template<class T>
bool Less(T x, T y)
{return x < y;
}template<>
bool Less<int*>(int* x, int* y)
{return *x < *y;
}

我们自己写了一个int*的特化Less函数,对于指针的比较就会走这个函数,不会再去走模板生成了。


但是写模板的特化,我不如直接实现函数重载,如下:

bool Less(int* x, int* y)
{return *x < *y;
}

或者直接实现指针模板的Less函数:

template<class T>
bool Less(T* x, T* y)
{return *x < *y;
}

3.2、类模板特化

3.2.1、全特化

全特化就是将类模板中的所有参数都确定化,如下:


template<class T1, class T2>
class Test
{
public:Test() { cout << "Test<T1, T2>" << endl; }
private:T1 _a1;T2 _a2;
};template<>
class Test<int, double>
{
public:Test() { cout << "Test<int, double>" << endl; }
};int main()
{Test<int, char> t1;Test<int, double> t2;return 0;
}

在这里插入图片描述
可以看到,t2走的是我们全特化的Test类。


3.2.2、偏特化

偏特化就是特化一部分参数,如下:

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

偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
例如限制Test类的两个参数为指针和引用类型:

template<class T1, class T2>
class Test<T1*, T2*>
{
public:Test() { cout << "Test<T1*, T2*>" << endl; }
};template<class T1, class T2>
class Test<T1&, T2&>
{
public:Test() { cout << "Test<T1&, T2&>" << endl; }
};

可以看下面main函数实例化出不同类型调用上面所写特化的不同类的构造函数。
在这里插入图片描述


3.2.3、类模板特化的应用

在这里插入图片描述


4、模板的分离编译

之前我们实现vector和list的时候,都是声明和定义写在一起。因为如果声明和定义分离在两个不同的文件中会出问题。
下面先给出代码:

// stack.h
#pragma once
#include <queue>namespace zzy
{template<class T, class Container = std::deque<T>>class stack{public:void push(const T& x);void pop();T& top(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};}
// stack.cpp
#include "stack.h"namespace zzy
{template<class T, class Container>void stack<T, Container>::push(const T& x){_con.push_back(x);}template<class T, class Container>void stack<T, Container>::pop(){_con.pop_back();}
}

在main函数中调用push/pop函数,就会出现如下错误:
在这里插入图片描述
这是因为,找不到push/pop函数的地址。因为stack.h和stack.cpp经过预处理、编译、汇编、链接然后形成可执行程序文件,但是在链接的时候,在符号表中找不到push/pop函数的地址,因为这里是模板函数,没有实例化出具体的类,所以也就没有具体函数代码 ,所以没有地址。

解决方案有两个:
1、显示实例化模板类

在这里插入图片描述
但是这种方式只能针对某种类型,当我换成stack<double>还是会报错,这时候就需要继续添加stack<double>的显示实例化。因此不推荐这种方法。

2、将声明和定义写在一起或写在同一个文件中
在这里插入图片描述
可以像size和empty一样将声明和定义直接写在一起。

在这里插入图片描述

也可以声明定义分离,但是写在同一个文件中。
推荐使用第二种方式,可以发现前面的vector、list声明和定义都是写在一个文件中的。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Rocky、Almalinux、CentOS、Ubuntu和Debian系统初始化脚本v9版

Rocky、Almalinux、CentOS、Ubuntu和Debian系统初始化脚本 Shell脚本源码地址&#xff1a; Gitee&#xff1a;https://gitee.com/raymond9/shell Github&#xff1a;https://github.com/raymond999999/shell脚本可以去上面的Gitee或Github代码仓库拉取。 支持的功能和系统&am…

Iotop使用

文章目录 Iotop依赖及编译1:内核配置2: 环境配置3.依赖库ncurses3.1 Ncurses的编译配置 4. Iotop的编译及修改5.测试效果如下&#xff1a; Iotop依赖及编译 源码路径&#xff1a;https://github.com/Tomas-M/iotop#how-to-build-from-source (GitHub - Tomas-M/iotop: A top u…

CVPR力推!预训练+医学图像这么玩,审稿人都得为你让条路!

最近发现Nature、CVPR、NeurIPS等顶会顶刊上&#xff0c;涌现了不少预训练医学图像的文章&#xff0c;不仅效果拔群&#xff0c;思路也很有启发性。 像是Nature上的REFERS&#xff0c;便颠覆了传统方法&#xff0c;使标注数据量直降90&#xff05;&#xff01;此外还有CVPR24上…

Spark 共享变量:广播变量与累加器解析

Spark 的介绍与搭建&#xff1a;从理论到实践_spark环境搭建-CSDN博客 Spark 的Standalone集群环境安装与测试-CSDN博客 PySpark 本地开发环境搭建与实践-CSDN博客 Spark 程序开发与提交&#xff1a;本地与集群模式全解析-CSDN博客 Spark on YARN&#xff1a;Spark集群模式…

基于Matlab 疲劳驾驶检测

Matlab 疲劳驾驶检测 课题介绍 该课题为基于眼部和嘴部的疲劳驾驶检测。带有一个人机交互界面GUI&#xff0c;通过输入视频&#xff0c;分帧&#xff0c;定位眼睛和嘴巴&#xff0c;通过眼睛和嘴巴的张合度&#xff0c;来判别是否疲劳。 二、操作步骤 第一步&#xff1a;最…

强化学习不愧“顶会收割机”!2大创新思路带你上大分,毕业不用愁!

强化学习之父Richard Sutton悄悄搞了个大的&#xff0c;提出了一个简单思路&#xff1a;奖励聚中。这思路简单效果却不简单&#xff0c;等于是给几乎所有的强化学习算法上了一个增强buff&#xff0c;所以这篇论文已经入选了首届强化学习会议&#xff08;RLC 2024&#xff09;&a…

个人记录。改错huggingface,离线使用

huggingface_hub.utils._errors.LocalEntryNotFoundError: Connection error, and we cannot find the requested files in the disk cache. Please try again or make sure your Internet connection is on. 下载 true改false

【计算机网络】网络框架

一、网络协议和分层 1.理解协议 什么是协议&#xff1f;实际上就是约定。如果用计算机语言进行表达&#xff0c;那就是计算机协议。 2.理解分层 分层是软件设计方面的优势&#xff08;低耦合&#xff09;&#xff1b;每一层都要解决特定的问题 二、网络传输基本流程 1.预备…

C++练习 字符串反转

从界面上输入一个C风格的字符串&#xff0c;如果输入的是"abc"&#xff0c;反转后"cba"。 要求&#xff1a; 1&#xff09;反转的结果存放在另一字符串中。 2&#xff09;原地反转&#xff0c;不借助其它的字符串。 #include <iostream> using n…

Postman常见问题及解决方法

软件测试资料领取&#xff1a;[内部资源] 想拿年薪40W的软件测试人员&#xff0c;这份资料必须领取~ 软件测试面试刷题工具&#xff1a;软件测试面试刷题【800道面试题答案免费刷】 1、网络连接问题 如果Postman无法发送请求或接收响应&#xff0c;可以尝试以下操作&#xf…

LED和QLED的区别

文章目录 1. 基础背光技术2. 量子点技术的引入3. 色彩表现4. 亮度和对比度5. 能效6. 寿命7. 价格总结 LED和 QLED都是基于液晶显示&#xff08;LCD&#xff09;技术的电视类型&#xff0c;但它们在显示技术、色彩表现和亮度方面有一些关键区别。以下是两者的详细区别&#xff…

光流法(Optical Flow)

一、简介 光流法&#xff08;Optical Flow&#xff09;是一种用于检测图像序列中像素运动的计算机视觉技术。其基于以下假设&#xff1a; 1.亮度恒定性假设&#xff1a;物体在运动过程中&#xff0c;其像素值在不同帧中保持不变。 2.空间和时间上的连续性&#xff1a;相邻像素之…

400. 第 N 位数字

目录 题目解法 题目 给你一个整数 n &#xff0c;请你在无限的整数序列 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …] 中找出并返回第 n 位上的数字。 解法 class Solution { public:int findNthDigit(int n) {int low 1, high 9;while (low < high) {int mid (high - lo…

FlinkPipelineComposer 详解

FlinkPipelineComposer 详解 原文 背景 在flink-cdc 3.0中引入了pipeline机制&#xff0c;提供了除Datastream api/flink sql以外的一种方式定义flink 任务 通过提供一个yaml文件&#xff0c;描述source sink transform等主要信息 由FlinkPipelineComposer解析&#xff0c…

10款音频剪辑推荐!!你的剪辑好帮手!!

在如今的数据化浪潮中&#xff0c;工作已经采用了线上线下相结合。我的工作就需要借助一些剪辑工具&#xff0c;来实现我对音频工具的剪辑。我初次接触到音频剪辑也是因为工作需求&#xff0c;从起初我只是一个音频剪辑的小白&#xff0c;这些工具的协助。吸引着我。对于这些工…

智能检测技术与传感器(热电传感器四个定律)

热电传感器&#xff1a; 两种不同的导体两端相互紧密地连接在一起&#xff0c;组成一个闭合回路。当两接点温度不等时&#xff08;设 &#xff09;&#xff0c;回路中就会产生大小和方向与导体材料及两接点的温度有关的电动势&#xff0c;从而形成电流&#xff0c;这种现象称为…

Ubuntu 20.04配置ollama并下载安装调用本地大语言模型

Ubuntu 20.04配置ollama并下载安装调用本地大语言模型 ollama 介绍(来自ChatGPT)主要特点 ollama开发环境预配置ollama在ubuntu下的安装直接安装压缩包安装创建开机ollama的脚本启动ollama ollama在ubuntu下的运行 ollama 介绍(来自ChatGPT) Ollama 是一种新的本地语言模型管理…

多点支撑:滚珠导轨的均匀分布优势!

滚珠导轨的滚珠稳定性可以有效保持滚珠导轨的稳定运行&#xff0c;减少滚珠脱落的风险&#xff0c;确保设备的长期稳定性和可靠性。事实上&#xff0c;滚珠导轨的滚珠稳定性主要依赖于以下几个方面&#xff1a; 1、精密的制造工艺&#xff1a;滚珠导轨的导轨和滑块通常采用高精…

轻松搭建在线文档管理系统:BookStack的Docker部署与远程访问指南

前言 本文将介绍如何在Linux系统上利用Docker本地部署在线文档管理系统BookStack&#xff0c;并通过cpolar内网穿透工具实现异地远程访问&#xff0c;无需公网IP或复杂的路由器设置。 BookStack是一个开源的知识管理平台&#xff0c;基于Laravel Vue.js构建。它提供了一个简…

【代码及应用】10个最常用的Python包!

世界上有超过200,000个Python程序包&#xff08;这只是基于官方的Python程序包索引PyPI托管的程序包&#xff09;这就引出了一个问题&#xff1a;拥有这么多的软件包&#xff0c;每个Python程序员都需要学习哪些软件包是最重要的&#xff1f; 包含编程资料、学习路线图、源代码…