C++——模板,template

news2024/12/23 9:41:12

函数模板

我们经常会遇到一种情况:用相同的方法处理不同的数据。对于是函数,我们可以用函数重载来解决。虽然重载可以解决这种情况,但还是很繁琐。如果函数重载10次,有一天你突然发现有新的需求,函数需要修改,那你只能把这10个函数依次修改了,麻烦的要死!所以函数模板他来了。

函数模板代表了一类函数,函数模板与参数类型无关,在使用时被实例化,根据参数类型产生特定类型的函数版本。


函数模板格式

template<typename T1, ... ,typename Tn>

函数返回值 函数名 (参数列表)   

{

        函数体;

}

注:typename可以用class替换,二者基本没有区别

        T1 ... Tn表示参数名,可以自己随便命名

Swap函数模板:

template<class T>
void Swap(T& a, T& b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

函数模板原理

函数模板不是函数,在编译器编译阶段,编译器会根据传入的实参类型推演生成对应的函数以供调用。所以我们调用的不是模板函数而是由模板函数生成的函数。


函数模板实例化

当我们使用函数模板时,生成函数供我们使用,这个过程就是函数的实例化。函数实例化分两种方式:隐式实例化和显式实例化。

隐式实例化

编译器自己推演模板参数类型

template<typename T>
T Add(T& a, T& b)
{
    return a + b;
}

int main()
{
    int a = 5;
    int b = 10;
    cout << Add(a, b) << endl;//15
}

显式实例化

如果加法函数的两个参数是不同的类型,隐式实例化会报错,因为模板参数T只能是一个类型。

解决这种情况有三种办法:

方法一:强制类型转换

template<typename T>
T Add(T& a, T& b)
{
    return a + b;
}

int main()
{
    int a = 5;
    double b = 10;
    cout << Add((double)a, b) << endl;
    cout << Add(a, (int)b) << endl;
}

方法二:使用两个模板参数

template<typename T1, typename T2>
T2 Add(T1 a, T2 b)
{
    return a + b;
}

int main()
{
    int a = 5;
    double b = 10;
    cout << Add(a, b) << endl;
}

 方法三:显式实例化

在函数名后<>指定模板参数的类型

template<typename T>
T Add(T a, T b)
{
    return a + b;
}

int main()
{
    int a = 5;
    double b = 10;
    cout << Add<int>(a, b) << endl;
    cout << Add<double>(a, b) << endl;
}

如果指定类型与实际类型不匹配,编译器会自动进行类型转换,无法转换就会报错


注意:

  1.  一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

类模板

类模板和函数模板类似

对于C++中用来存储数据的container,例如vector,string,list,stack等等,都是类模板。container可以存储各种不同的数据,不管是自定义类型还是内置类型。我们使用这些container存储具体的数据就是一种类模板的实例化。

//类模板定义
template<class T1,...,Tn>
class classname
{
    ...
}

//类模板实例化

void test()
{
    //type是任意的数据类型
    classname<type> c;
    ...
}

typename和class的区别

二者在大部分情况下没有区别,但是这种情况下会不同:

当模板参数是容器内部定义的类型参数,并且在后面也使用了该参数。那么在使用该参数时必须加typename前缀

template<class T, class container=vector<T>>
//在后面的类中使用了 容器中的类型 的模板参数
class it
{
private:
	container c;
public:
	void print()
	{
        //必须使用typename前缀
		typename container::iterator _it = c.begin();
		while (_it != c.end())
		{
			cout << *_it << ' ';
			_it++;
		}
	}
	void pushback(const T& x)
	{
		c.push_back(x);
	}
};
在模板中使用了 容器中的类型 的模板参数

 原因:

当我们用::使用容器中的类型时,编译器无法判断它是不是一个类型,它也可能是容器中的一个静态成员,typename就是告诉编译器这是一个类型,编译才可以过。


非类型模板参数

在模板中有两种参数,类型形参和非类型形参。模板中的非类型参数可以当作常量使用,这种参数多用于静态容器,即不需要扩容,使用时指定容器大小的情况。

但是有几点要注意:

1.非类型参数必须是整形,浮点数和类对象以及字符串不允许

2.非类型参数必须在编译器就能确定结果

下面是用非类型模板参数模拟实现静态数组的代码:

template<class T, size_t num = 10>
class arr
{
private:
	T _a[num];
	size_t _size;
public:
	T& operator[](size_t index)
	{
		return _a[index];
	}
	const T& operator[](size_t index)const
	{
		return _a[index];
	}
	size_t size()const
	{
		return _size;
	}
	bool empty()const
	{
		return _size == 0;
	}
};

模板的特化

有的时候模板实例化后在大部分情况下是正确的,但是个别情况下无法正常使用或者需求不同,这就需要我们对个别情况下的模板进行特化,即特殊实例化。

函数模板特化(不支持偏特化)

template<class T1, class T2>
T1 add(const T1 x1, const T2 x2)
{
	return x1 + x2;
};
//特化
template<>
int add<int, double>(const int x1, const double x2)
{
	return x1 - x2;
};
void test3()
{
	cout << add(4, 2.1) << endl;
	cout << add(4, 1) << endl;
}

类模板特化 

template <class T1, class T2>
class Data
{
private:
	T1 d1;
	T2 d2;
public:
	void print()
	{
		cout << "Data(T1, T2)" << endl;
	}
};
//全特化
template<>
class Data<int, int>
{
private:
	int d1;
	int d2;
public:
	void print()
	{
		cout << "Data(int, int)" << endl;
	}
};
//偏特化,对部分模板参数特化
template<class T2>
class Data<int, T2>
{
private:
	int d1;
	T2 d2;
public:
	void print()
	{
		cout << "Data(int, T2)" << endl;
	}
};
//偏特化,对模板参数进行进一步限制
template<class T1, class T2>
class Data<T1*, T2>
{
private:
	T1* d1;
	T2 d2;
public:
	void print()
	{
		cout << "Data(T1*, T2)" << endl;
	}
};
void test4()
{
	Data<double, double> d1;
	d1.print();
	Data<int, double> d2;
	d2.print();
	Data<int, int> d3;
	d3.print();
	Data<int*, double> d4;
	d4.print();
}

模板的分离编译

一个程序由许多源文件共同构成,每个源文件分别编译成目标文件,最后链接到一起形成一个可执行程序,这是分离编译模式。

下面的模板分离编译会有什么问题?

com.h
//声明
template <class T>
bool compare(const T& left, const T& right);

com.cpp
//定义
template <class T>
bool compare(const T& left, const T& right)
{
    return left < right;
}

test.cpp
#include"com.h"
int main()
{
	compare(1, 2);
	compare(3, 2);
    return 0;
}

com.cpp编译时,函数未实例化,不会生成compare函数,也就没有函数地址;因为调用了compare<int>函数,test.cpp在链接时要寻址,但是找不到compare函数的地址,就会报错。

解决方法:

将函数声明和定义放到一个xxx.hpp或xxx.h文件中即可。

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

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

相关文章

Linux CentOS7 tree命令

tree就是树&#xff0c;是文件或文件名输出到控制台的一种显示形式。 tree命令作用&#xff1a;以树状图列出目录的内容&#xff0c;包括文件、子目录及子目录中的文件和目录等。 我们使用ll命令显示只能显示一个层级的普通文件和目录的名称。而使用tree则可以树的形式将指定…

管理类联考——数学——汇总篇——知识点突破——代数——等比数列——性质

下标和定理 在等比数列中&#xff0c;若 m &#xff0b; n p q ( m &#xff0c; n &#xff0c; p &#xff0c; q ∈ N &#xff0b; ) m&#xff0b;npq(m&#xff0c;n&#xff0c;p&#xff0c;q∈N_&#xff0b;) m&#xff0b;npq(m&#xff0c;n&#xff0c;p&#x…

面经学习三

目录 Java 与 C 的区别 面向对象和面向过程的区别 面向对象特性 Java的基本数据类型 深拷贝和浅拷贝 Java创建对象的几种方式 final, finally, finalize 的区别 Java 与 C 的区别 Java 是纯粹的面向对象语言&#xff0c;所有的对象都继承自 java.lang.Object&#xff0c…

mybatis学习记录(三)-----关于SQL Mapper的namespace

关于SQL Mapper的namespace 视频总结笔记&#xff1a; 在SQL Mapper配置文件中<mapper>标签的namespace属性可以翻译为命名空间&#xff0c;这个命名空间主要是为了防止SQL id 冲突的。 创建CarMapper2.xml文件&#xff0c;代码如下&#xff1a; CarMapper2.xml: <?…

天机学堂项目微服务架构实战

1.学习背景 各位同学大家好&#xff0c;经过前面的学习我们已经掌握了《微服务架构》的核心技术栈。相信大家也体会到了微服务架构相对于项目一的单体架构要复杂很多&#xff0c;你的脑袋里也会有很多的问号&#xff1a; 微服务架构该如何拆分&#xff1f;到了公司中我需要自己…

JDK14特性——概述语法变化

文章目录 Java14概述语法变化&#xff1a;instanceof语法变化&#xff1a;switch表达式语法变化&#xff1a; 文本块的改进语法变化&#xff1a; Records记录类型Records限制Records额外的变量类型 Java14概述 Oracle在2020年3月17日宣布JAVA14 全面上市&#xff0c;JAVA14通过…

Anomalib库安装以及使用

Anomalib: A Deep Learning Library for Anomaly Detection PDF&#xff1a;https://arxiv.org/pdf/2202.08341.pdf 代码&#xff1a;https://github.com/openvinotoolkit/anomalib 1 概述 Anomalib是一个专注于异常检测的深度学习库。它的目标是收集最新的异常检测算法&…

旧版office如何卸载干净,Mac电脑移除office教程

版office卸载不干净导致无法激活新版Microsoft office&#xff0c;这个问题如何解决呢&#xff1f;深受这一烦恼的小伙伴看过来&#xff01; 旧版office由于证书一直清理不干净&#xff0c;电脑上有旧证书存在导致新版offce激活不成功&#xff0c;具体手动清理方法带给大家。 …

Makefile 神奇:驾驭编译的力量

一.make和Makefile 当谈到 make 和 Makefile 时&#xff0c;通常是指构建工具 make 和用于描述编译和构建过程的文本文件 Makefile。 make 是一个在类Unix系统中广泛使用的构建工具。它基于文件的时间戳比较&#xff0c;只编译发生了变化的文件&#xff0c;从而提高了编译效率…

软考和PMP哪个含金量更高?

软考中&#xff0c;能和pmp一起来比较的是软考高项&#xff0c;软考高级信息系统项目管理师&#xff0c;和PMP的共同点&#xff0c;基本来说都是项目管理类的证书。本质也都是适用于项目经理岗位的证书&#xff0c;软考高项中大部分考试内容是PMPIT技术两部分&#xff0c;其中项…

JDK19特性

文章目录 JAVA19概述1. 记录模式(预览版本)2.Linux/RISC-V 移植3.外部函数和内存 API &#xff08;预览版&#xff09;4.虚拟线程(预览版)5.Vector API &#xff08;第四次孵化&#xff09;6.Switch 模式匹配&#xff08;第三预览版&#xff09;7.结构化并发&#xff08;孵化阶…

uniapp视频播放功能

UniApp提供了多种视频播放组件&#xff0c;包括视频播放器&#xff08;video&#xff09;、多媒体组件&#xff08;media&#xff09;、WebView&#xff08;内置Video标签&#xff09;等。其中&#xff0c;video和media组件是最常用的。 video组件 video组件是基于HTML5 vide…

北工大汇编——子程序设计

题目要求 完成一个字母或数制之间的转化程序&#xff0c;主程序分别具有 5种可选择的子功能&#xff0c;按相应的宇符可分别进入相应的子功能并在屏幕上显示结果&#xff0c;按“q”键退出。子功能分别为&#xff1a; 1&#xff09; 实现小写字母向大写字母的转换&#xff1b;…

Cesium与Threejs融合

融合demo 一、简介 将Cesium与three.js进行融合,从而是3d具备大场景GIS能力,使GIS具备3d能力。 关键步骤如下: 1、局部坐标系定义和坐标转换 2、相机同步 3、事件同步 二、代码 <script setup lang="ts"> import { onMounted } from vue import @ano…

批量剪辑的视频重复率高怎么去重?有可以批量剪出原创视频、脚本创作、矩阵分发的软件推荐吗?

对于批量剪辑生成的视频&#xff0c;大多朋友都会为重复率发愁&#xff0c;速度是快了&#xff0c;但是视频都是重复的&#xff0c;发出去效果不好又有什么用呢&#xff1f; 网上也能看到许多人提出&#xff0c;对素材进行改变&#xff0c;比如更换不一样的BGM&#xff0c;修改…

【MongoDB】docker部署社区版(一)

0、背景介绍 项目中使用MongoDB了&#xff0c;服务器挂掉&#xff0c;自己在本地搭一个试试。 1、版本选择 首先有社区版和和商业版。我选的是社区版。链接&#xff1a;https://hub.docker.com/r/mongodb/mongodb-community-server/tags 1.1、标签选择 看到标签有两个大类…

23 DRF快速入门+部分源码分析

文章目录 前言知识前后端不分离前后端分离RESTful APIHTTP请求方法详解部分状态码&#xff08;常见&#xff09; Django补充知识创建多个app 安装安装完后的配置 Drf框架介绍相比于Django的优势基础介绍--快速入门--了解框架的大概实现Serializers基础准备编写Serializers.py 以…

S1FD40A180H-ASEMI快恢复二极管S1FD40A180H

编辑&#xff1a;ll S1FD40A180H-ASEMI快恢复二极管S1FD40A180H 型号&#xff1a;S1FD40A180H 品牌&#xff1a;ASEMI 封装&#xff1a;TO-247 特性&#xff1a;大功率、快恢复二极管 正向电流&#xff1a;40A 反向耐压&#xff1a;1800V 恢复时间&#xff1a;<300n…

【全志V3s】SPI NAND Flash 驱动开发

文章目录 一、硬件介绍V3s的启动顺序 二、驱动支持U-Boot驱动主线 Linux 驱动已经支持 三、烧录工具 xfel四、构建U-Boot&#xff08;官方的Uboot&#xff09;先编译一下开始spi nand flash 代码层面的适配修改menuconfig配置ARM architecture配置Support for SPI Nand Flash o…

Windows的远程桌面 Server+frp 配置

前言 通过华为云耀服务器给的优惠&#xff0c;购买了一个镜像实例&#xff0c;用来配置远程桌面服务&#xff0c;采用frp进行内网穿透&#xff0c;云服务环境为Ubuntu22.04。 配置 1.服务器端frp配置 cd /usr/local # 进入默认的程序安装路径 wget clone https://github…