C++:模板详解

news2024/11/19 22:47:23

模板详解

  • 1.函数模板
    • 1.概念
    • 2.语法
    • 3.原理
    • 4.实例化
      • 1.隐式实例化
      • 2.显示实例化
    • 5.匹配原则
  • 2.类模板
    • 1.格式
    • 2.实例化
  • 3.非类型模板参数
    • 注意点
  • 4.特化
    • 1.概念
    • 2.函数模板特化
      • 1.前提
      • 2.语法说明
      • 3.示例
    • 3.类模板特化
      • 1.全特化
      • 2.偏特化/半特化
      • 3.选择顺序
    • 4.按需实例化
  • 5.模板的分离编译
    • 1.分离编译
    • 2.模板的分离编译
      • 问题
      • 分析问题
      • 解决方案
    • 3.模板的两次编译
  • 6.总结
    • 优点
    • 缺点

1.函数模板

1.概念

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

2.语法

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){ ...}
template<typename T>
void Swap( T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

3.原理

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。

4.实例化

1.隐式实例化

通过传入参数的类型让编译器自己推理。

2.显示实例化

自己手动写出传入的类型。在函数名后的<>中指定模板参数的实际类型

注意:如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

int main()
{
	int a = 10;
 	double b = 20.0;
 	// 显式实例化
	Add<int>(a, b);
 	return 0;
}

5.匹配原则

有现成的(现有的函数重载)使用现成的,没有现成的就使用模板实例化。

注意:模板函数不允许隐式类型转换,但普通函数可以。

int Add(int left, int right)
{
	return left + right;
}

template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
 	return left + right;
}

void Test()
{
 	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
 	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数,而不会进行隐式类型转换
}

2.类模板

1.格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{
 // 类内成员定义
}; 

2.实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

vector<int> s1;

3.非类型模板参数

在之前模板参数都是给的类型,比如Int,double之类,但是我们也可以在模板参数之中给一个常量。

namespace test
{
	// 定义一个模板类型的静态数组
 	template<class T, size_t N = 10>
 	class array
 	{
	public:
		T& operator[](size_t index){return _array[index];}
		const T& operator[](size_t index)const{return _array[index];}
 		size_t size()const{return _size;}
 		bool empty()const{return 0 == _size;}
	private:
		T _array[N];
		size_t _size;
 	}}

比如在实现静态顺序表时,其的空间大小是确定的,使用N来确定,如果需要同时创建一个数组大小为10和大小为1000的顺序表呢?我们只能将N改为1000来满足需求,但是这样会浪费一定的空间。如果可以将常量也可以作为模板参数使用,那么我们分别需要一个数组大小为10和数组大小为1000的顺序表时,可以通过传入参数来确定大小,从而不会浪费多余的空间。

注意点

1.非类型模板参数默认只能给整型家族的,直到C++20以后才支持double,string等

2.非类型模板参数必须在编译的时候就能确定大小因为在编译期就需要开辟空间。

4.特化

由于模板,我们可以实现出与类型无关的代码,来实现相同的功能,但是对于某些类型,使用模板的逻辑会产生我们预期之外的结果,这种类型需要特殊处理,因此产生了特化。

1.概念

在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化

2.函数模板特化

1.前提

必须有函数模板—>模板特化不能单独存在

2.语法说明

template<>
返回值类型 函数名<需要特化的类型> (形参1,....)
{
}

注意:函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。

3.示例

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

template<>
bool Less<int*>(int* x, int* y)
{
	cout << "Less<int*>(int* x, int* y)" << endl;
	return *x < *y;
}
template<class T>
bool Less(T* x, T* y)
{
	cout << "Less(T* x, T* y)" << endl;
	return *x < *y;
}
template<class T>
bool Less(T* x, T* y)
{
	cout << "Less(T* x, T* y)" << endl;
	return *x < *y;
}
int main()
{
	int* a = new int(4);
	int* b = new int(5);
	cout << Less(a, b) << endl;
	double* c = new double(1.1);
	double* d = new double(2.2);
	cout << Less(c, d) << endl;
	return 0;
}

在这里插入图片描述

总结:尽量不要使用函数模板的特化,因为语法等各种比较复杂,如果有需要,直接函数重载即可。

注意:其调用规则为有重载调用重载,没有重载再看特化中是否有符合的,如果特化中没有调用模板实例化的函数。

3.类模板特化

1.全特化

全特化就是把该类模板的所有参数特化(确定化)。

//函数模板
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;
};

2.偏特化/半特化

偏特化有两种形式:

1.部分参数特化
	部分特化就是把该类模板的一部分参数确定化。
2.参数限制(即类型特化)
	参数限制特化就是对该类模板的参数符合一定格式要求的确定化。(比如正常类型走模板实例化,指针类型走特化等)
//函数模板
template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

//参数半特化
template<class T1>
class Data<T1, char>
{
public:
	Data() { cout << "Data<T1, char>" << endl; }
private:
	T1 _d1;
	char _d2;
};

//类型限制
template<class T1,class T2>
class Data<T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }
private:
	T1 _d1;
	char _d2;
};

int main()
{
	Data<int, int> d1;
	Data<int, char> d2;
	Data<int*, double*> d3;
	return 0;
}

在这里插入图片描述

3.选择顺序

有现成的(即特化好的)使用现成的,没有现成的使用模板实例化出来的。

4.按需实例化

按需实例化就是编译器在实例化时用到哪个函数才会实例化哪个函数。

情景:

//array.h
namespace test
{
	// 定义一个模板类型的静态数组
	template<class T, size_t N = 10>
	class array
	{
	public:
		T& operator[](size_t index) { 
			size(1); //此处是有一个错误的!!!
			return _array[index]; 
		}
		size_t size()const { return _size; }
		bool empty()const { return 0 == _size; }
	private:
		T _array[N];
		size_t _size = 0;
	};
}
//test.cpp
int main()
{
	test::array<int, 100> a;
	cout << a.empty() << endl;
    cout << a.size() << endl;
    a[10];
	return 0;
}

上面的代码如果运行起来是没有报错的,因为模板是一个半成品,编译器在预处理之后,编译之前会对模板的大体框架进行粗略检查,比如有无分号括号等,但是不会检查内部细节,比如上面size()中参数个数不一致,在创建a这个对象时实例化了默认构造函数,由于我们只调用empty和size(),因此只实例化了这两个函数,没有实例化错误的operator[],就不会报错,除非我们调用它。

5.模板的分离编译

1.分离编译

分离编译就是每一个源文件都是独立编译生成其各自的目标文件,然后所有的目标文件在链接过程中才会整合最后形成可执行文件。

2.模板的分离编译

如果一个类使用了模板,然后其的函数声明和定义分开,声明在.h文件,定义在.cpp文件,那么运行后编译器会报错:无法解析的外部符号。

问题

//array.h
#pragma once
#include<iostream>
using namespace std;
namespace Array
{
	// 只支持整形做非类型模板参数
	// 非类型模板参数  类型 常量
	// 类型模板参数   class 类型
	template<class T, size_t N = 10>
	class array
	{
	public:
		size_t size() const;
	private:
		T _array[N];
		size_t _size = 0;
	};
	void func();
}


//array.cpp
#include"array.h"
namespace Array
{
	template<class T, size_t N>
	size_t array<T, N>::size() const
	{
		T x = 0;
		x += N;
		return _size;
	}
	void func()
	{
		cout << "I am func" << endl;
	}
}

//test.cpp
#include"array.h"
int main()
{
	Array::array<int, 100> a;
	cout << a.size() << endl;

	Array::func();
}

运行后报错:

在这里插入图片描述

分析问题

出现这种链接错误是因为找不到这个函数的地址。

可是我们已经定义了该函数,为什么会没有它的地址呢?

由于每一个文件都是分离编译生成对应的目标文件,然后再进行链接。

对于func函数,array.h在test.cpp文件中展开,由于有函数的声明,并且其也有定义,在生成符号表时会正确的加入符号表从而被查找到。

对于size函数,array.h在test.cpp文件中展开,其有函数声明,在编译时不会报语法错误,但是在链接时,由于调用了size(),因此需要去符号表找其对应的地址去调用,但是我们的size()定义时只是一个模板,并没有实例化出来,因此在符号表中无法找到对应的函数去调用,因此报链接错误。

那么我们明明已经在test.cpp中实例化了对象,为什么size()没有实例化呢?

因为每一个文件是分离编译的!!!只有test.cpp文件知道array这个类需要用int和100来实例化,但是array.cpp并不知道,因此也不会实例化test.cpp在链接时直接去找自然找不到。

解决方案

1.显示实例化

即在.cpp文件中加入这样的代码:

template
array<int, 100>;

这样可以显示告诉编译器需要实例化出的案例,这样这样在分离编译时,array.cpp才会实例化出对应的函数。

但是不推荐这样的写法。因为如果这样,那么每一次实例化出类型不同的类,都需要再次进行显示实例化。

2.将声明和定义写在同一个.h中(强烈推荐!!!)

因为array.h在test.cpp中展开时,由于声明和定义在一个文件,展开后也在一个文件,那么自然知道需要实例化成什么类型。

3.模板的两次编译

模板的两次编译是指:
第一次在预处理之后,编译之前,会进行按需实例化,第二次是在编译的时候对实例化的部分进行语法排查。

6.总结

优点

1.模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2.增强了代码的灵活性

缺点

1.模板会导致代码膨胀问题,也会导致编译时间变长
2.出现模板编译错误时,错误信息非常凌乱,不易定位错误

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

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

相关文章

玄子Share-计算机网络参考模型

玄子Share-计算机网络参考模型 分层思想 利用七层参考模型&#xff0c;便于在网络通信过程中&#xff0c;快速的分析问题&#xff0c;定位问题并解决问题 将复杂的流程分解为几个功能相对单一的子过程 整个流程更加清晰&#xff0c;复杂问题简单化 更容易发现问题并针对性的…

Labview2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 LabVIEW是一种由美国国家仪器&#xff08;NI&#xff09;公司开发的程序开发环境&#xff0c;它显著区别于其他计算机语言&#xff0c;如C和BASIC。传统的计算机语言是基于文本的语言来产生代码&#xff0c;而LabVIEW则采用图形化…

【Python】函数基础(纯干货版)

目录 什么是函数 函数定义 函数的文档说明 局部变量和全局变量 综合案例&#xff1a;模拟实现ATM界面 什么是函数 函数是组织好的&#xff0c;可重复使用的&#xff0c;用于实现特定功能的代码段&#xff0c;将功能封装在函数内&#xff0c;可供随时随地重复利用&#xff…

代理IP对网络爬虫有什么影响?

代理IP对网络爬虫的影响深远且多方面&#xff0c;主要体现在以下几个方面&#xff1a; 第一点&#xff0c;代理IP能有效防止爬虫IP被封禁&#xff1a;在爬虫工作过程中&#xff0c;如果频繁访问同一目标网站&#xff0c;很容易被该网站的服务器识别为恶意行为&#xff0c;导致…

Pytest精通指南(22)钩子函数-重复执行(pytest-repeat)

文章目录 前言应用场景插件安装参数分析使用方式一&#xff1a;命令行使用方式二&#xff1a;配置文件使用方式三&#xff1a;装饰器 前言 pytest框架中的**重复测试&#xff08;pytest-repeat&#xff09;**插件的用途是允许在运行测试用例时进行多次循环&#xff0c;以更全面…

阿里二面凉了,难蹦。。。

分享一位同学阿里巴巴的后端面经&#xff0c;共有 2 面&#xff0c;第一面很顺利过了&#xff0c;可惜挂在第二面。 这两面的知识点范围&#xff0c;我帮大家罗列一下&#xff1a; 网络&#xff1a;TCP、HTTP mysql&#xff1a;索引应用、索引结构、隔离级别、最左匹配 redis…

流程控制:goto语句,模拟switch语句

示例&#xff1a; /*** brief how about goto-switch? show you here.* author wenxuanpei* email 15873152445163.com(query for any question here)*/ #define _CRT_SECURE_NO_WARNINGS//support c-library in Microsoft-Visual-Studio #include <stdio.h>static …

循环购模式:创新消费返利,引领电商新潮流

大家好&#xff0c;我是吴军&#xff0c;今天我将与大家探讨一种别具一格的商业模式——循环购模式。或许您曾经听说过消费满额赠送现金的活动&#xff0c;甚至每天都能累积并提取部分现金。您可能会好奇&#xff0c;商家为何如此慷慨地“回馈”消费者&#xff1f;这背后其实蕴…

QT C++ sqlite 对多个数据库的操作

//本文描述&#xff0c;QT 对多数据库的操作。 //你可能会想&#xff0c;多数据库的操作时&#xff0c;查询语句怎么知道是哪个数据库。 //QT提供了这样一种构造函数 QSqlQuery(const QSqlDatabase &db) //指定数据库 //在QT6.2.4 MSVC2019调试通过。 //效果见下图&am…

刷题 替换数字

题干 给定一个字符串 s&#xff0c;它包含小写字母和数字字符&#xff0c;请编写一个函数&#xff0c;将字符串中的字母字符保持不变&#xff0c;而将每个数字字符替换为number。 例如&#xff0c;对于输入字符串 "a1b2c3"&#xff0c;函数应该将其转换为 "an…

关于超出表示范围的数据类型转化

目录 背景&#xff1a; 问题分析&#xff1a; 参数异常分析&#xff1a; 分析文件原始值&#xff1a; 分析数据类型转换 代码分析&#xff1a; 结论&#xff1a; 参考资料&#xff1a; 背景&#xff1a; 在Ubuntu环境下进行项目开发时&#xff0c;调试时程序总是进入断…

小型企业网络优化加速方案

随着数字化经济蓬勃发展&#xff0c;小型企业的网络基础设施变得尤为重要。在这一浪潮中&#xff0c;建立一个稳定、高效的企业网络成为支撑业务发展的关键。本文将深入研究针对小型企业设计的网络优化加速方案&#xff0c;助力企业主了解如何规划和实施适合自身业务需求的网络…

车载诊断系统应用方案选型,ESP8266方案让成本降低了35%,销售数据提升47%

车载诊断系统简称OBD&#xff0c;这个系统随时监控发动机的运行状况和尾气后处理系统的工作状态&#xff0c;一旦发现有可能引起排放超标的情况&#xff0c;会马上发出警示。当系统出现故障时&#xff0c;故障灯(MIL)或检查发动机(Check Engine)警告灯亮&#xff0c;同时OBD系统…

使用Python进行容器编排Docker Compose与Kubernetes的比较

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 随着容器化技术的普及&#xff0c;容器编排成为了管理和部署容器化应用程序的重要环节。在容…

尺取法知识点讲解

一、固定长度的情况&#xff1a; 最小和(sum) 输入N个数的数列&#xff0c;所有相邻的M个数的和共有N-M1个&#xff0c;求其中的最小值。 输入格式 第1行&#xff0c;2个整数N&#xff0c;M&#xff0c;范围在[3…100000]&#xff0c;N>M。 第2行&#xff0c;有N个正…

Sqli-labs靶场第25关[Sqli-labs-less-25]自动化注入-SQLmap工具注入

过滤了AND OR 使用的函数是 preg_replace 特点&#xff1a;只对值进行一次检测闭合方式为 单引号 可以使用双写进行绕过 手工注入 ?id0 union select 1,database(),user() -- sqlmap自动化注入 sqlmap.py -u http://192.168.58.114:802/sqli-labs/Less-25/?id2 --batch -…

交换机的种类有哪些?主要都具有哪些作用?

在当今数字化时代&#xff0c;网络已经成为我们生活和工作中不可或缺的一部分。无论是家庭网络还是企业网络&#xff0c;都需要有效的网络设备来实现数据通信和资源共享。而网络交换机作为一种重要的网络设备&#xff0c;扮演着连接和管理网络设备的关键角色。本文将探讨交换机…

etcd campaign

1. 引言 本文主要讲解使用etcd进行选举的流程&#xff0c;以及对应的缺陷和使用场景 2. etcd选举流程 流程如以代码所示&#xff0c;流程为&#xff1a; clientv3.New 创建client与etcd server建立连接 concurrency.NewSession 创建选举的session&#xff0c;一般会配置ses…

【LAMMPS学习】八、基础知识(3.5)计算弹性常数

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

公众号推文链接二维码如何制作?网址转换二维码的操作技巧

如何将公众号文章的链接做成二维码图片呢&#xff1f;公众号是很多人会使用的一种推广手段&#xff0c;通过更新文章来为关注公众号的用户提供内容。那么如果想要将已发布的推文的网址生成二维码&#xff0c;让用户可通过扫码的方式来访问对应链接的文章&#xff0c;这种效果该…