关于模板的大致认识【C++】

文章目录

  • 函数模板
    • 函数模板的原理
    • 函数模板的实例化
    • 模板参数的匹配原则
  • 类模板
    • 类模板的定义格式
    • 类模板的实例化
  • 非类型模板参数
  • typename 与class
  • 模板的特化
    • 函数模板特化
    • 类模板特化
      • 全特化
      • 偏特化
  • 模板的分离编译

函数模板

函数模板的原理

template <typename T> //模板参数 ——类型 
void Swap(T& x1, T& x2)
{T tmp = x1;x1 = x2;x2 = tmp;
}
int main()
{int a = 0, b = 1;double c = 1.1, d = 2.2;swap(a, b);swap(c, d);int* p1 = &a;int* p2 = &b;swap(p1, p2);return 0;
}

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

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此

函数模板的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化

1、隐式实例化:让编译器根据实参推演模板参数的实际类型

template<class T>
T Add(const T& left , const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.1, d2 = 20.2;//函数模板根据调用,自动推导模板参数的类型 ,实例化对应的参数 cout << Add(a1, a2) << endl;cout << Add(d1, d2) << endl;//实参传递的类型, 推演T的类型  cout << Add( a1, (int)d1  ) << endl;cout << Add( (double)a1, d1) << endl;//显示实例化 ,用指定的类型实例化 ,相当于隐式类型转换 cout << Add<int> (a1, d1) << endl;cout << Add<double>(a1, d1) << endl;return 0;
}

2、显式实例化:在函数名后的<>中指定模板参数的实际类型

template<class T>
T Add(const T& left , const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.1, d2 = 20.2;//函数模板根据调用,自动推导模板参数的类型 ,实例化对应的参数 cout << Add(a1, a2) << endl;cout << Add(d1, d2) << endl;//实参传递的类型, 推演T的类型  cout << Add( a1, (int)d1  ) << endl;cout << Add( (double)a1, d1) << endl;//显示实例化 ,用指定的类型实例化 ,相当于隐式类型转换 cout << Add<int> (a1, d1) << endl;cout << Add<double>(a1, d1) << endl;return 0;
}
template<class T >T * Alloc(int n )
{return new T[n];
}int main(){//有些函数无法自动推导函数模板的类型,实例化对应的参数,只能显式实例化double *p1 = Alloc <double>(10);return 0;}

模板参数的匹配原则

类模板

类模板 ,无法推演实例化,所以类模板都是显式实例化

class Stack
{
public :Stack(int capacity = 3){_array= new  T[capacity];_size = 0;_capacity = 0; }void Push(const T &  data){_array[_size++] = data;}~Stack(){free(_array);_size = _capacity = 0;}
private :T * _array;int _size;int _capacity;
};
int main()
{Stack <int> s1(); // int Stack <double> s2();//double Stack <char> s3();//char//Stack <int ,doule> s2();return 0; 
}

类模板的定义格式

函数类模板的声明和定义分离

template<class T>class Stack
{
public://声明 Stack(int capacity );void Push(const T& data){_array[_size++] = data;}~Stack(){free(_array);_size = _capacity = 0;}
private:T* _array;int _size;int _capacity;
};
//定义 
template<class T>Stack<T>::Stack(int capacity )
{_array = new  T[capacity];_size = 0;_capacity = 0;
}
int main()
{Stack <int> s1(); // int Stack <double> s2();//double Stack <char> s3();//char//Stack <int ,doule> s2();return 0;
}

对于普通类,类名和类型是一样的,但是对于类模板 ,类名和类型是不一样的 上面的代码中Stack是类名 ,但是Stack < T >是类型

类模板的实例化

非类型模板参数

模板参数可分为类型形参非类型形参

类型形参: 出现在模板参数列表中,跟在class或typename关键字之后的参数类型名称。

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

如果此时有一个需求,实现一个静态数组的类,就需要用到非类型模板参数

template<class T, size_t N> //N:非类型模板参数
// N是常量 ,且N必须是整形
class StaticArray
{
public:size_t arraysize(){return N;}
private:T _array[N]; //利用非类型模板参数指定静态数组的大小
};
int main()
{StaticArray<int, 10> a1; //定义一个大小为10的静态数组cout << a1.arraysize() << endl; //10StaticArray<int, 100> a2; //定义一个大小为100的静态数组cout << a2.arraysize() << endl; //100return 0;
}

注意:

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

typename 与class

一般来说,typename 和class 没有什么区别,但是在有一种情景下是有区别的

template<class Container>
void Print(  const Container& v  )
{//Container::const_iterator it = v.begin();是不行的// 因为编译不确定Container::const_iterator是类型还是对象// typename的作用就是明确告诉编译器这里是类型,等模板实例化再去找typename Container::const_iterator it = v.begin();while (it != v.end() ){cout << *it << " ";it++;}cout << endl;
}int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);Print(v);list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);Print(lt1);return 0;
}

以上情景需要使用typename

模板的特化

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

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

函数模板特化

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

template<class T>
bool Less(T left ,T right)
{return left < right;
}
//函数模板的特化
template<>
bool Less<int * >(int * left, int* right)
{return *left < *right;
}
int main()
{int a = 1, b = 2;cout << Less(1, 2);cout << endl;cout << Less(&a, &b);return 0;
}

类模板特化

不仅函数模板可以进行特化,类模板也可以针对特殊类型进行特殊化实现,并且类模板的特化又可分为全特化和偏特化(半特化)。

全特化

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

例如,对于以下类模板:

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;
};void TestVector()
{Data<int, int> d1;Data<int, char> d2;
}

偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。

偏特化有以下两种表现方式:
1、部分特化 ,将模板参数类表中的一部分参数特化

template<class T1, class T2>
class Data
{
public:Data(){cout<<"Data<T1, T2>" <<endl;}
private:T1 _d1;T2 _d2;
};// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:Data() {cout<<"Data<T1, int>" <<endl;}
private:T1 _d1;int _d2;
};
int main()
{Data<double, int> d1;//偏特化Data<int, double> d2;//调用基础的模板return 0;
}

2、参数更进一步的限制

template<class T1, class T2>
class Data
{
public:Data(){cout<<"Data<T1, T2>" <<endl;}
private:T1 _d1;T2 _d2;
};
//偏特化:对类型的进一步限制
template<class T1, class T2>
class Data<T1*, T2*>
{
public:Data() { cout << "Data<T1*, T2*>" << endl; }
private:
};int main()
{Data<int, int > d1;Data<int, double > d2;Data<int*, double > d3;Data<int*, double* > d4;Data<void*, void* > d5;return 0;
}

模板的分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

在分离编译模式下,我们一般创建三个文件

一个头文件用于进行函数声明
一个源文件用于对头文件中声明的函数进行定义
最后一个源文件用于调用头文件当中的函数

举个例子:如果对一个加法函数模板进行分离编译

在这里插入图片描述

如果这三个文件生成可执行文件时,会在链接阶段产生报错
这是为什么呢?

C / C++程序要运行起来一般要经历以下四个步骤:
预处理
编译
汇编
链接
如果需要详细的了解这四个步骤,请点击这里

在这里插入图片描述

这三个文件经过预处理后就只剩下两个文件了

Visual Studio平台:

预处理后就进行编译,
虽然在 main.i 当中有调用Add函数的代码,但是在 main.i 里面也有Add函数模板的声明,因此在编译阶段并不会发现任何语法错误,之后便顺利将 Add.i 和 main.i 翻译成了汇编语言,

到汇编阶段,此阶段利用 Add.s 和 main.s 这两个文件分别生成了两个目标文件,

前面的预处理、编译和汇编都没有问题,现在就需要将生成的两个目标文件进行链接操作了,但在链接时发现,在main函数当中调用的两个Add函数实际上并没有被真正定义,主要原因是函数模板并没有生成对应的函数,因为在全过程中都没有实例化过函数模板的模板参数T,所以函数模板根本就不知道该实例化T为何类型的函数。

模板分离编译失败的原因:
在函数模板定义的地方(Add.cpp)没有进行实例化,而在需要实例化函数的地方(main.cpp)没有模板函数的定义,无法进行实例化。

Linux平台下:

预处理后就需要进行编译,就生成了 Add.s 和 main.s 文件。

汇编:生成了 Add.o 和 main.o 两个目标文件。

如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注你们的每一次支持都将转化为我前进的动力!!!

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

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

相关文章

2023年最佳JavaScript框架:React、Vue、Angular和Node.js的比较

文章目录 React&#xff1a;构建用户界面的首选Vue&#xff1a;简单优雅的前端框架Angular&#xff1a;Google支持的全面框架Node.js&#xff1a;服务器端的JavaScript运行环境比较不同框架的优势与劣势React&#xff1a;Vue&#xff1a;Angular&#xff1a;Node.js&#xff1a…

KaiwuDB CTO 魏可伟:回归用户本位,打造“小而全”的数据库

8月16日&#xff0c;KaiwuDB 受邀亮相第十四届中国数据库技术大会 DTCC 2023。KaiwuDB CTO 魏可伟接受大会主办方的采访&#xff0c;双方共同围绕“数据库架构演进、内核引擎设计以及不同技术路线”展开深度探讨。 以下是采访的部分实录 ↓↓↓ 40 多年前&#xff0c;企业的数…

Lua之Lua源文件批量转换为luac字节码文件

准备的工具&#xff1a;luac.exe CSDNhttps://mp.csdn.net/mp_download/manage/download/UpDetailed Unity版: using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine;public static class Bat…

Postman 如何进行参数化

前言 Postman作为一款接口测试工具&#xff0c;受到了非常多的开发工程师的拥护。 那么做为测试&#xff0c;了解Postman这款工具就成了必要的了。 这篇文章就是为了解决Postman怎么进行参数化的。 全局变量 全局变量是将这个变量设置成整个程序的都可以用&#xff0c;不用去…

Coremail参与编制|《信创安全发展蓝皮书——系统安全分册(2023年)》

信创安全发展蓝皮书 近日&#xff0c;Coremail参与编制的《信创安全发展蓝皮书—系统安全分册&#xff08;2023年&#xff09;》重磅发布。 此次信创安全发展蓝皮书由工业和信息化部电子第五研究所联合大数据协同安全技术国家工程研究中心重磅共同发布。 本次蓝皮书涵盖信创系…

开发一个npm组件包

vue项目初始化 vue create mytest 启动项目以后 组件开发 开发的组件写在 package中 如下如例 开发一个 listpage的组件 里面放了一个a链接注册组件配置打包 "package": "vue-cli-service build --target lib ./src/package/index.js --name managerpage -…

大数据——一文熟悉HBase

1、HBase是什么 HBase是基于HDFS的数据存储&#xff0c;它建立在HDFS文件系统上面&#xff0c;利用了HDFS的容错能力&#xff0c;内部还有哈希表并利用索引&#xff0c;可以快速对HDFS上的数据进行随时读写功能。 Hadoop在已经有一个HiveMapReduce结构的数据读写功能&#x…

【数据分析】波士顿矩阵

波士顿矩阵是一种用于分析市场定位和企业发展战略的管理工具。由美国波士顿咨询集团&#xff08;Boston Consulting Group&#xff09;于1970年提出&#xff0c;并以该集团命名。 波士顿矩阵主要基于产品生命周期和市场份额两个维度&#xff0c;将企业的产品或业务分为四个象限…

LabVIEW模拟化学反应器的工作

LabVIEW模拟化学反应器的工作 近年来&#xff0c;化学反应器在化学和工业过程领域有许多应用。高价值产品是通过混合产品&#xff0c;化学反应&#xff0c;蒸馏和结晶等多种工业过程转换原材料制成的。化学反应器通常用于大型加工行业&#xff0c;例如酿酒厂公司饮料产品的发酵…

多维时序 | MATLAB实现SCNGO-BiLSTM-Attention多变量时间序列预测

多维时序 | MATLAB实现SCNGO-BiLSTM-Attention多变量时间序列预测 目录 多维时序 | MATLAB实现SCNGO-BiLSTM-Attention多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | MATLAB实现SCNGO-BiLSTM-Attention多变量时间序列预测。 模型描…

Hadoop集群搭建(hadoop-3.3.5)

一、修改服务器配置文件 1、配置环境变量 vim /etc/profile #java环境变量 export JAVA_HOME/usr/local/jdk/jdk8 export JRE_HOME$JAVA_HOME/jre export CLASSPATH$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH export PATH$JAVA_HOME/bin:$JRE_HOME/bin:$PATH #hadoop环境变量 …

SQLSTATE[IMSSP]: The active result for the query contains no fields.

我的是SQL server 报错场景&#xff0c;代码&#xff1a; $psendmx_sql"SET IDENTITY_INSERT PSENDMX ON;INSERT INTO psendmx (DJBH,MIBH,MXBH,SPDM,GG1DM,GG2DM,SL,SL_2,CKJ,ZK,DJ,DJ_1,JE,HH) VALUES {$mx_values};SET IDENTITY_INSERT PSENDMX OFF;"; $a$db_er…

线性代数的学习和整理---番外1:EXCEL里角度,弧度,三角函数

目录 1 角的度量&#xff1a;角度和弧度 1.1 角度 angle 1.1.1 定义 1.1.2 公式 1.1.2 角度取值范围 1.2 弧长和弦长 1.3 弧度 rad 1.3.1 弧长和弧度定义的原理 1.3.2 定义 1.3.3 取值范围 1.3.4 取值范围 1.4 角度&#xff0c;弧度的换算 1.5 EXCEL里进行角度和…

k8s ingress (二)

k8s ingress (二) Ingress介绍 在前面课程中已经提到&#xff0c;Service对集群之外暴露服务的主要方式有两种&#xff1a;NodePort和LoadBalancer&#xff0c;但是这两种方式&#xff0c;都有一定的缺点&#xff1a; NodePort方式的缺点是会占用很多集群机器的端口&#xff0…

Apollo自动驾驶:引领未来的智能出行

自动驾驶技术正日益成为当今科技领域的焦点&#xff0c;它代表着未来出行的一大趋势&#xff0c;而Baidu公司推出的Apollo自动驾驶平台则在这一领域中展现出强大的领导地位。本文将深入探讨Apollo自动驾驶技术的关键特点、挑战以及它对未来智能出行的影响。 Apollo自动驾驶平台…

SpringBoot(二)

###SpringBoot原理分析 ###SpringBoot监控 ###SpringBoot项目部署 #SpringBoot自动配置 Condition&#xff1a;&#xff08;条件&#xff09; Condition是在Spring4.0增加的条件判断功能&#xff0c;通过这个功能可以实现选择性的创建Bean操作 SpringBoot是如何知道要创建…

设计模式-观察者模式

观察者模式 ● 行为设计模式&#xff0c;允许对象存在的一种一对多的关系 ● 当一个对象发生变化&#xff0c;所有依赖他的对象都会得到通知并自动更新。 ● 在这种模式中&#xff0c;发生状态变化的对象叫作“主题”&#xff0c;依赖他的对象被称为“观察者” 观察者例子的简…

【VS】InstallerProjects.vsix下载 Microsoft Visual Studio Installer Projects (2022)

InstallerProjects.vsix 是微软官方提供的winform程序打包工具&#xff0c;但是国内下载安装有时候比较慢。虽然只有5m左右&#xff0c;但是国内就是下载不下来。现将官网地址和下载后的百度网盘共享地址展示如下&#xff1a;方便大家使用 官方地址&#xff1a;https://market…

再JAVA中如何使用qsort对类进行排序?

目录 结论&#xff1a; 解析&#xff1a; 结论&#xff1a; import java.util.Arrays;class Person implements Comparable<Person>{public String name;public int age;public Person(String name, int age) {this.name name;this.age age;}Overridepublic Stri…

# Lua与C++交互(二)———— 交互

C 调用lua 基础调用 再来温习一下 myName “beauty girl” C想要获取myName的值&#xff0c;根据规则&#xff0c;它需要把myName压入栈中&#xff0c;这样lua就能看到&#xff1b;lua从堆栈中获取myName的值&#xff0c;此时栈顶为空&#xff1b;lua拿着myName去全局表中查…