深入了解模板知识(c++)

news2024/11/15 11:55:14

前言

        在c++中模板是很重的,泛型编程就是模板最好的体现,模板的出现就是为了更好的复用代码,有了它,我们不必写各种逻辑相同只是逻辑中的数据的类型的不同的代码,使得我们编写代码变得更加高效,下面让我们一起来深入的理解和模板有关的知识。

目录

1.非类型模板参数

2.模板的特化 

        2.1函数模板的特化

                2.1.1什么叫函数模板的特化 

                2.1.2函数模板特化的步骤 

        2.2类模板的特化 

                2.2.1什么叫类模板的特化

                2.2.2全特化

                2.2.3偏特化

                2.2.4类模板特化的应用示例

3.模板在分离编译时的问题

        3.1什么是分离编译

        3.2模板的分离编译

        3.3解决法 

 4.总结

        模板的优点:

        缺点:


1.非类型模板参数

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

        类型形参是出现在模板参数列表中跟在class或者typename后面的参数类型名称。

        非类型形参是指在模板形参列表中的一个作为形参的常量,在函数模板或者类模板中可以将该参数当做常量使用。

        如果有这样的需求:需要定义同类型的对象,一个对象中成员变量中的数组的大小为10,另一个对象的成员变量中的数组的大小为20,这时候你就可能需要在模板参数中加入非类型形参:

#include<iostream>
using namespace std;
template <class T,size_t N = 7>//模板中含有非类型的参数
class A
{
public:
	cout << a;
private:
	T _arr[N];
	size_t _size;
};
int main()
{
	return 0;
}

        注意:非类型模板参数是一个常量,该常量可以是整形,字符型,短整型等整形家族的内置类型不可以是其他类型(可以这样理解这种非类型模板参数一般是作为改变静态数组(固定大小的数组)的大小来用的,让其他类来做非类型模板参数没有太大的意义),语法不允许。

        例如:

#include<iostream>
using namespace std;
template <class T,size_t N = 7>
class A
{
public:
	cout << a;
private:
	T _arr[N];
	size_t _size;
};
int main()
{
	return 0;
}

        此时就会有语法错误。

2.模板的特化 

        2.1函数模板的特化

                2.1.1什么叫函数模板的特化 

        一般情况下使用模板可以实现一些与类型无关的代码,但是对于一些特殊的类型可能会出现错误的情况,此时就需要我们做一些特殊的处理,比如实现一个专门用来进行小于比较的函数模板。如下:

template <class T1, class T2>
bool Greater(const T1 & x1,const T2 & x2)
{
	return x1 > x2;
}
int main()
{
	int a1 = 3;
	int b1 = 5;
	cout << (a1 > b1)<<endl;//如果比较内置类型可以得到正确的结果
	const char* p1 = "bcdefg";
	//但是比较其他的可能就会出现错误
	const char* p2 = "bcdef";
	cout << (p1 > p2)<<endl;
	return 0;
}

         可以发现Greater在大多数情况下是正常的,但是在一些特殊的情况下会出现问题,比如在比较两个字符串的大小时,这时候就需要我们对这种特殊的情况进行特殊的处理,也就是对函数模板进行特化,对模板进行特化就是在原来模板的基础上,针对特殊的类型所进行的特殊处理,进行特殊化的实现方式。模板特化分为函数特化和类特化。

        例如: 

template<>//特化针对某些类型进行特殊化处理
bool Greater<char *>(char * &p1, char * &p2)
{
	if (strcmp(p1, p2) > 0)
		return true;
	else
		return false;
}

                2.1.2函数模板特化的步骤 

        1.必须要有一个基础的模板(没有特化的模板)

        2.关键字template后面接一对尖括号

        3.函数名后面跟一对尖括号,尖括号中指定特化的类型。

        4.函数形参表必须和模板函数的基础参数类型完全相同,如果不同,编译器会报出一些奇奇怪怪的错误。

例如:

template <class T>
bool Greater( T&  x1, T&  x2)
{
	return x1 > x2;
}
template<>//特化针对某些类型进行特殊化处理
bool Greater<char *>(char * &p1, char * &p2)
{
	if (strcmp(p1, p2) > 0)
		return true;
	else
		return false;
}
int main()
{
	int a = 5;
	int b = 7;
	cout << Greater(a, b);//调非特化的模板函数
	const char* p1 = "abcde";
	const char* p2 = "accde";
	cout << Greater(p1, p2);//调用特化的模板函数

	return 0;
}

        2.2类模板的特化 

                2.2.1什么叫类模板的特化

                当模板参数为某种特殊类型时模板会实例化出特殊的对象。

        例如:

template<class T1, class T2>
class A
{
public:
	A()
	{
		cout << "非特化模板类  " << _a << endl;
	}
private:
	T1 _a = 0;
	T2 _b;
};
template<>
class A<int,char>
{
public:
	A()
	{
		cout << "特化模板类  " << _a << endl;
	}
private:
	int _a = 0;
	char _b;
};
int main()
{
	A <int, int>a1;//调用非特化的模板类实例化对象
	A<int, char> a2;//调用特化的模板类实例化对象
	return 0;
}

                2.2.2全特化

        全部的模板参数都进行特化。

        例如:

template<class T1, class T2>
class A
{
public:
	A()
	{
		cout << "非特化模板类  " << _a << endl;
	}
private:
	T1 _a = 0;
	T2 _b;
};
template<>
class A<int,char>//全特化
{
public:
	A()
	{
		cout << "特化模板类  " << _a << endl;
	}
private:
	int _a = 0;
	char _b;
};

                2.2.3偏特化

        任何对模板参数进一步进行条件限制的特化版本。偏特化有两种表现形式:1.部分特化,例如:

template<class T2>
class A<int, T2>//全特化
{
public:
	A()
	{
		cout << "特化模板类  " << _a << endl;
	}
private:
	int _a = 0;
	T2 _b;
};
int main()
{
	A<int, double> a3;//调用部分特化的模板类实例化对象
	return 0;
}

2. 对参数更进一步的限制,例如:

template<>//对参数做出限制,如果是两个指针参数就会实例化这个模板
class A<class T1*, class T2*>
{
public:
	A()
	{
		cout << "参数进行限制的特化模板类  " << endl;
	}
private:
	T1* _a = nullptr;
	T2* _b = nullptr;
};
int main()
{
	A<char*, char*> a4;//调用对参数进行限制的模板类实例化出对象
	return 0;
}

偏特化不仅仅是对模板参数部分特化,也可以是对模板参数的进一步限制。 

                2.2.4类模板特化的应用示例

 例如有一下专门用来比较小于的类模板Less:

#include<algorithm>
template<class T>
struct Less
{
	bool operator()(const T& x1, const T& x2)const
	{
		return x1 < x2;
	}
};
int main()
{
	int arr[] = { 2,3,4,1,6,5,8,7,9 };
	sort(arr, arr + 9, Less<int>());//对数组进行排序
	for (auto& e : arr)
		cout << e << " ";
	return 0;
}

 但是如果用上面的Less类对

指针数组的内容 进行排序就会出现排序的结果错误,例如:

    int main()
    {
        const char* p1 = "abcde";
	    const char* p2 = "abbde";
	    const char* p3= "accde";

	    const char* parr[]= {p1,p2,p3};
	    sort(parr, parr + 3, Less<const char*>()); 
    }

这时候就需要对Less类模板进行特化处理。 如下:

#include<algorithm>
#include<string.h>
template<class T>
struct Less
{
	bool operator()(const T& x1, const T& x2)const
	{
		return x1 < x2;
	}
};
template<>
struct Less< char*>
{
	bool operator()(char* x1,  char* x2)const
	{
		if (strcmp(x1, x2) < 0)
			return true;
		else
			return false;
	}
};
int main()
{
	int arr[] = { 2,3,4,1,6,5,8,7,9 };
	sort(arr, arr + 9, Less<int>());//对数组进行排序
	for (auto& e : arr)
		cout << e << " ";
	 char p1[] = "abcde";
	 char p2[] = "abbde";
	 char p3[]= "accde";

	char* parr[]= {p1,p2,p3};
 	sort(parr, parr + 3, Less<char*>());//调用特化的less进行排序
	for(auto&e:parr)
		cout << e << " ";
	return 0;
}

3.模板在分离编译时的问题

        3.1什么是分离编译

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

        3.2模板的分离编译

        如果将模板分离编译会产生什么结果呢?我们一起试试,

//Func.h
#pragma once
#include<iostream>
using namespace std;

void F1();
template<class T>
T Add(const T& x1, const T& x2);//模板函数的申明
//Func.cpp

#include"Func.h"
void F1()
{
	cout << "普通函数" << endl;
}
template<class T>
T Add(const T& x1, const T& x2)
{
	return x1 + x2;
}
//Test.cpp
#include"Func.h"
int main()
{
	F1();
	Add(1, 2);//调用函数模板
	return 0;
}

         运行程序,程序会报出以下错误:

        这是为什么呢?我们来一起探究一下:

        在c/c++程序要运行,一般要经历一下几个步骤:

        1.预处理,2.编译,3.汇编,4.链接。这四个步骤都做了什么事情呢?详见:编译和链接详解 

        预处理阶段:会进行头文件的展开,注释的删除,条件编译,define定义的符号和函数的替换。

        编译阶段:对程序按照语言特性,进行词法,语法,语义的分析,错误检查无误后生成汇编代码,编译器对工程中的多个源文件是分离开单独编译的。

       链接阶段:将多个obj文件合并成一个,并处理没有解决的地址问题。

        如图:

 

        3.3解决法 

        解决方法有两种,

        第一种是,将定义和声明放在同一个文件"xxx.hpp"里面或者"xxx.h"其实也可以。推荐使用这种。

        第二种是模板定义的位置的显示实例化。例如:

#include"Func.h"
void F1()
{
	cout << "普通函数" << endl;
}
template<class T>
T Add(const T& x1, const T& x2)
{
	return x1 + x2;
}
//显示实例化
template
int  Add<int>(const int& x1, const int& x2);

        第二种方法的缺点在于,如果想要用模板实例化出其他类型的函数时,又要在模板定义的位置显示实例化其他的类型,不够方便。 例如:

#include"Func.h"
int main()
{
	Add(2.3, 4.5);//调用两个double类型的值进行相加
	return 0;
}
#include"Func.h"
void F1()
{
	cout << "普通函数" << endl;
}
template<class T>
T Add(const T& x1, const T& x2)
{
	return x1 + x2;
}
//显示实例化
template
int  Add<int>(const int& x1, const int& x2);

//显示实例化double类型的Add函数
template
double Add<double>(const double& x1, const double& x2);

 

 4.总结

        模板的优点:

        1.增强了代码的复用性,节省资源,易于更快的开发,c++标准库(STL)因此而产生。

        2.增加了代码的灵活性。

        缺点:

        1.模板会导致代码的膨胀问题,也会导致编译时间增加(因为要通过模板实例化出具体的函数或者类)

        2.出现模板编译错误时,错误信息非常凌乱,不易定位错误。

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

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

相关文章

若依权限系统分析(前后端分离版)

若依权限系统分析 一&#xff1a;故事背景二&#xff1a;具体权限控制2.1 页面权限控制2.2 页面元素权限控制 三&#xff1a;实现前端鉴权3.1 封装js与权限交互3.1.1 uni-app自带uni-request与权限交互 3.2 vux状态管理3.2.1 自定义状态3.2.2 在vuex的store配置内添加我们新增的…

rust切片

这里s的不可变引用借用给了wordIndex&#xff0c;而s.clear()又想用可变引用&#xff0c;所以报错。而第一个例子中返回的usize并没有返回不可变引用。

客户端负载均衡工具Ribbon

一 什么是Ribbon Ribbon介绍 目前主流的负载方案分为以下两种&#xff1a; 集中式负载均衡&#xff0c;在消费者和服务提供方中间使用独立的代理方式进行负载&#xff0c;有硬件的&#xff08;比如 F5&#xff09;&#xff0c;也有软件的&#xff08;比如 Nginx&#xff09;…

Ubuntu系统中分布式安装配置HBase-2.3.7

HBase是一个基于Hadoop的分布式列式数据库&#xff0c;可以存储海量的结构化和半结构化数据。本文介绍如何在三个Ubuntu系统上搭建一个HBase集群&#xff0c;并进行简单的数据操作。 在三个Ubuntu系统上分布式安装配置HBase-2.3.7&#xff0c;主要步骤包括&#xff1a; 准备工…

MySQL的执行原理

一、单表访问之索引合并 我们前边说过MySQL在一般情况下执行一个查询时最多只会用到单个二级索引&#xff0c;但存在有特殊情况&#xff0c;在这些特殊情况下也可能在一个查询中使用到多个二级索引&#xff0c;MySQL中这种使用到多个索引来完成一次查询的执行方法称之为&#…

Qgis加载在线XYZ瓦片影像服务的实践操作

目录 背景 一、XYZ瓦片相关知识 1、xyz瓦片金字塔 2、 瓦片编号 3、瓦片访问 二、在Qgis中加载在线地图 1、Qgis版本 2、瓦片加载 3、地图属性预览 总结 背景 在做电子地图应用的时候&#xff0c;很常见的会提到瓦片&#xff08;tile&#xff09;的概念&#xff0c;瓦片…

Java实训日志07

文章目录 八、项目开发实现步骤&#xff08;十&#xff09;创建应用程序类1、创建app子包2、创建Application类 &#xff08;十一&#xff09;创建窗口界面类1、创建主界面窗口&#xff08;1&#xff09;做一个空白的主界面窗口&#xff08;2&#xff09;退出时弹出消息框询问用…

【cutlass】cuTe layout操作

简介 cuTe提供了对Layout操作的算法&#xff0c;可以混合执行来构建更复杂的Layout操作&#xff0c;比如在其他layout之间切分和平铺layout 在host或者device上打印cuTe cuTe的打印函数可以在host和device端打印。cute::print 重载了几乎所有 CuTe 类型&#xff0c;包括指针…

MT8168/MTK8168核心板,4G安卓核心板

MT8168是一款集成度很高的高性能应用处理器&#xff0c;具有低功耗特性&#xff0c;并且提供卓越的多媒体体验&#xff0c;适用于平板电脑、智能手持终端以及智能家居和物联网应用等嵌入式设备。这款芯片采用了先进的12纳米工艺&#xff0c;将四核Arm-Cortex A53 MPCore TM CPU…

关于JAVA中 方法中无法改变String的分析

package com.atguigu.String01;public class String01 {public static void main(String[] args) {// 字符串不变性String str "hello";// 对象成员数组是finalchange(str);System.out.println("change后的str:"str);int[] a {1,3,5,7,9};int[] b {2,3,…

【V4L2】 v4l2框架分析之v4l2_fh

一、v4l2_fh简介 &#x1f53a;相关源码文件&#xff1a; /drivers/media/v4l2-fh.c /drivers/media/v4l2-fh.h 在V4L2中&#xff0c;struct v4l2_fh结构用于保存V4L2框架中使用的文件句柄&#xff08;File Handle&#xff09;的数据&#xff0c;即每个打开的视频设备都会对…

微信小程序开发入门学习01-TDesign模板解读

目录 1 使用模板创建小程序2 app.json3 页面布局总结 原来我们使用微信开发者工具&#xff0c;比较困难的是前端框架的选择上&#xff0c;官方也没有提供一套框架供我们使用&#xff0c;最近开发者工具已经提供了一套前端框架&#xff0c;后续我们开发的效率会因为使用模板提高…

Linux-线程的同步与互斥

线程的同步与互斥 进程/线程间的互斥相关背景概念互斥量互斥量接口互斥量的初始化互斥量的销毁加锁和解锁 改善抢票系统互斥量原理 可重入与线程安全重入和线程安全的概念常见线程不安全情况常见线程安全的情况常见不可重入情况常见可重入情况可重入与线程安全的关系可重入与线…

Spring Security系列之认证(Authentication)架构

文章目录 架构主要组件SecurityContextHolderAuthenticationAuthenticationManagerProviderManagerAuthenticationProviderAuthenticationEntryPointAbstractAuthenticationProcessingFilter 架构主要组件 SecurityContextHolder - SecurityContextHolder 是 Spring Security …

【tensorflow】连续输入的神经网络模型训练代码

【tensorflow】连续输入的神经网络模型训练代码 全部代码 - 复制即用 训练输出 代码介绍 全部代码 - 复制即用 from sklearn.model_selection import train_test_split import tensorflow as tf import numpy as np from keras import Input, Model, Sequential from keras.l…

try-catch-finally中的四大坑

目录 1.坑1&#xff1a;finally中使用return 2.坑2&#xff1a;finally中的代码好像“不执行” 3.坑3&#xff1a;finally中的代码“非最后”执行 4.坑4&#xff1a;finally中的代码真的“不执行” 在 Java 语言中 try-catch-finally 看似简单&#xff0c;但想要真正的“掌…

对现在的生活不满意?《围城》给你个人,婚姻,爱情的启示

杨绛先生在100岁感言的时候说&#xff0c;我们曾如此期盼外界的认可&#xff0c;到最后才知道&#xff1a;世界是自己的&#xff0c;与他人毫无关系&#xff01;百岁老人的感言&#xff0c;清晰透彻地道出了人生的真相。我们每个人都是生活于关系之中的&#xff0c;在错综复杂的…

华为OD机试真题 JavaScript 实现【找车位】【2023 B卷 100分】,附详细解题思路

一、题目描述 停车场有一横排车位&#xff0c;0代表没有停车&#xff0c;1代表有车。至少停了一辆车在车位上&#xff0c;也至少有一个空位没有停车。 为了防剐蹭&#xff0c;需为停车人找到一个车位&#xff0c;使得距停车人的车最近的车辆的距离是最大的&#xff0c;返回此…

【tensorflow】连续输入的线性回归模型训练代码

【tensorflow】连续输入的感知机模型训练 全部代码 - 复制即用 训练输出 代码介绍 全部代码 - 复制即用 from sklearn.model_selection import train_test_split import tensorflow as tf import numpy as np from keras import Input, Model, Sequential from keras.layers …

2023ciscn初赛 Unzip

参考&#xff1a; 奇安信攻防社区-2021深育杯线上初赛官方WriteUp-Web篇 1.打开环境 2.上传一个文件&#xff0c;得到以下源码&#xff0c;分析一下8 <?php error_reporting(0); highlight_file(__FILE__);$finfo finfo_open(FILEINFO_MIME_TYPE); //使用 PHP 内置函数…