C++初阶学习之模板进阶

目录
  • 一、非模板类型参数
  • 二、模板特化
    • 1、函数模板特化
    • 2、类模板特化
      • 1)全特化
      • 2)偏特化
  • 三、模板分离编译
  • 四、模板总结
  • 总结

一、非模板类型参数

分类:

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

概念:

1.类型形参:

出现在模板参数列表中,跟在class或者typename之类的参数类型名称

2.非类型形参:

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

示例:

namespace cole
{
	// 定义一个模板类型的静态数组
	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;
	};
}

注意:

1.浮点数、类对象以及字符串是不允许作为非类型模板参数的

2.非类型的模板参数必须在编译期就能确认结果

二、模板特化

概念:

使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果

示例:

template<class T>
bool IsEqual(const T& left, const T& right)
{
	return left == right;
}
// 函数模板的特化 (针对某些类型的特殊化处理)
//bool IsEqual(const char* const & left,const char* const & right)
bool IsEqual(const char* left, const char* right)
{
	return strcmp(left, right) == 0;
}
int main()
{
	cout << IsEqual(1, 2) << endl;
	char p1[] = "hello";
	char p2[] = "hello";
	cout << IsEqual(p1, p2) << endl;;
	return 0;
}

特殊化结果:

 不做特殊化结果:

注:此时对于字符串比较就需要对模板进行特化(在原模板类的基础上,针对特殊类型所进行特殊化的实现方式)

特化分类:

函数模板特化类模板特化

1、函数模板特化

函数模板的特化步骤:

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

示例:

template<class T>
bool IsEqual(const T left, const T right)
{
	return left == right;
}
template<>
bool IsEqual<char*>(char* left,char* right)
{
	if (strcmp(left, right) == 0)
		return true;
	return false;
}

结果:

注:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出

示例:

bool IsEqual(char* left, char* right)
{
	if (strcmp(left, right) == 0)
		return true;
	return false;
}

2、类模板特化

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

2)偏特化

概念:

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

偏特化有以下两种表现方式:

1.部分特化

将模板参数类表中的一部分参数特化

示例:

// 将第二个参数特化为inttemplate <class T1>class Data<T1, int>{public:Data() { cout << "Data<T1, int>" << endl; }private:T1 _d1;int _d2;};// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
	Data() { cout << "Data<T1, int>" << endl; }
private:
	T1 _d1;
	int _d2;
};

2.参数更进一步的限制

偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本

示例:

//两个参数偏特化为指针类型template <typename T1, typename T2>class Data <T1*, T2*>{public:Data() { cout << "Data<T1*, T2*>" << endl; }private:T1 _d1;T2 _d2;};//两个参数偏特化为引用类型template <typename T1, typename T2>class Data <T1&, T2&>{public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl;}private:const T1& _d1;const T2& _d2;};void test2(){Data<double, int> d1; // 调用特化的int版本Data<int, double> d2; // 调用基础的模板Data<int*, int*> d3; // 调用特化的指针版本Data<int&, int&> d4(1, 2); // 调用特化的指针版本}//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}
private:
	const T1& _d1;
	const T2& _d2;
};
void test2()
{
	Data<double, int> d1; // 调用特化的int版本
	Data<int, double> d2; // 调用基础的模板
	Data<int*, int*> d3; // 调用特化的指针版本
	Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}

三、模板分离编译

分离编译概念:

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

模板分离编译:

假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义

示例:

// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
// main.cpp
#include"a.h"
int main()
{
	Add(1, 2);
	Add(1.0, 2.0);
	return 0;
}

注:以上代码的模板分离编译会报错

分析:

解决方法

1.将声明和定义放到一个文件 “xxx.hpp” (h文件和cpp文件结合)里面或者xxx.h其实也是可以的(推荐)

2.模板定义的位置显式实例化(不实用)

四、模板总结

优点:

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

缺陷:

  • 模板会导致代码膨胀问题,也会导致编译时间变长(为了尽量减少此类问题,编译器会按需实例化)
  • 出现模板编译错误时,错误信息非常凌乱,不易定位错误
  • 不支持分离编译

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C++ 标准模板类详解

    目录 1 标准模板库 2.泛型编程 总结 1 标准模板库 STL提供了表示容器.迭代器.函数对象和算法的模板. 容器:类似数组存储若干值,切实同质的: 迭代器:遍历容器的对象,类似遍历数组的指针,广义指针: 算法:完成特定的任务: 函数对象:类对象或函数指针. 模板类 vector erase() 删除矢量中给定区间元素.接受两个迭代器参数(该参数定义了要删除的区间),迭代器1指向区间起始处,迭代器2指向区间终止处的后一个位置. // delete first and second elemen

  • C++中模板和STL介绍详解

    目录 一.模板 1.1.函数模板 1.1.1.两种函数模板的实例化 1.1.2.模板参数的匹配原则 1.2.类模板 二.STL 总结 一.模板 对于一个交换函数,虽然C++支持函数重载,我们可以对多个交换函数起相同的名字: void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double& left, double& right) { doub

  • C++入门之模板基础讲解

    目录 前言 引入 模板 函数模板 模板的匹配原则 模板的显示调用 类模板 注意1 注意2 总结 前言 今天博主将要介绍的内容是–模板,他在C++中具有非常重要的位置.至于什么是模板呢?我们请看下面的章节. 引入 我们对交换函数Swap已经非常熟悉了,但是我们经常会遇到这样的一些事,比如,很多不同的数据类型进行交换,那么我们就需要写不同的重载Swap,如下: #include <iostream> using namespace std; void Swap(int& a,int&

  • C++ 函数模板和类模板详情

    目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式化 2.3 函数模板原理 2.4 函数模板实例化 2.5 模板参数的匹配原理 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 1. 泛型编程 如何实现一个通用的交换函数? 在C++中可以用到函数重载 class A { public: void Swap(int& x1,int& x2) { int temp=x1; x1=x2; x2=temp; } void Swap(double&

  • C++函数模板的使用详解

    函数模板可以适用泛型来定义函数,其中泛型可以是(int, double, float)等替换.在函数重载过程中,通过将类型作为参数传递给模板,可使编译器自动产生该类型的函数. 工作原理:比如需要定义一个比大小的max函数,有三种类型的数据(int,double,float),可能就需要编写三个函数,这样既浪费时间,且容易出错.如: #include <iostream> using namespace std; int Max(int a, int b); double Max(double

  • C++初阶学习之模板进阶

    目录 一.非模板类型参数 二.模板特化 1.函数模板特化 2.类模板特化 1)全特化 2)偏特化 三.模板分离编译 四.模板总结 总结 一.非模板类型参数 分类: 模板参数分类类型形参与非类型形参 概念: 1.类型形参: 出现在模板参数列表中,跟在class或者typename之类的参数类型名称 2.非类型形参: 用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用 示例: namespace cole { // 定义一个模板类型的静态数组 template<cla

  • Python Matplotlib初阶使用入门教程

    目录 0. 前言 1. 创建Figure的两种基本方法 1.1 第1种方法 1.2 第2种方法 2. Figure的解剖图及各种基本概念 2.1 Figure 2.2 Axes 2.3 Axis 2.4 Artist 3. 绘图函数的输入 4. 面向对象接口与pyplot接口 5. 绘图复用实用函数例 0. 前言 本文介绍Python Matplotlib库的入门求生级使用方法. 为了方便以下举例说明,我们先导入需要的几个库.以下代码在Jupyter Notebook中运行. %matplotl

  • C++初阶之list的模拟实现过程详解

    list的介绍 list的优点: list头部.中间插入不再需要挪动数据,O(1)效率高 list插入数据是新增节点,不需要增容 list的缺点: 不支持随机访问,访问某个元素效率O(N) 底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低. 今天来模拟实现list 我们先来看看官方文档中对于list的描述 我们先大致了解一下list的遍历 迭代器 对于迭代器我们可以用while循环+begin()end().同时还可以用迭代器区间. 当然迭代器区间的方式只适用于内存连续的结构

  • C语言初阶之数组详细介绍

    目录 插入排序讲解 二维数组 二维数组的初始化 二维数组的访问 n维数组 字符数组 字符数组和字符串 字符数组的输入输出 字符串函数的简单使用 综合使用字符串函数 总结 插入排序讲解 #include<stdio.h> int main() { int arr[8] = { 1,2,3,4,6,7,10 }; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); int n = 0; scanf("%d", &n); f

  • C语言新手初阶教程之三子棋实现

    目录 三子棋 创建项目环境 头文件内容 编写main函数(test.c) 实现每一个接口函数 1.board 2.printfboard 3.play 4.computerplay 5.win 总结 三子棋 大家小时候应该都玩过三子棋吧,学习了这么久的C语言,我们其实已经具备了做三子棋这种小型项目的能力,今天我会尽量沉浸式的教大家实现三子棋,如果看完这篇博客还是不会的可以去我最后附上的gitee仓库链接中寻找.但我还是希望大家能自己完成,在三子棋中体现自己的风格. 创建项目环境 首先,第一步,打

  • C++初阶教程之类和对象

    目录 类和对象<上> 1. 类的定义 2. 类的封装 2.1 访问限定修饰符 2.2 类的封装 3. 类的使用 3.1 类的作用域 3.2 类的实例化 4. 类对象的存储 5. this 指针 5.1 this 指针的定义 5.2 this 指针的特性 类和对象<中> 1. 构造函数 1.2 构造函数的定义 2.2 构造函数的特性 2. 析构函数 2.1 析构函数的定义 3. 拷贝构函数 3.1 拷贝构造函数的定义 3.2 拷贝构造函数的特性 4. 运算符重载 4.1 运算符重载的

  • jqGrid 学习笔记整理——进阶篇(一 )

    在浏览导航栏添加所需按钮 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>DEMO</title> <link rel="stylesheet" type="text/css" href="css/jquery-ui.min.css" /> <link rel=&

  • Java中jqGrid 学习笔记整理——进阶篇(二)

    相关阅读: Java中jqGrid 学习笔记整理--进阶篇(一) 本篇开始正式与后台(java语言)进行数据交互,使用的平台为 JDK:java 1.8.0_71 myEclisp 2015 Stable 2.0 Apache Tomcat-8.0.30 Mysql 5.7 Navicat for mysql 11.2.5(mysql数据库管理工具) 一.数据库部分 1.创建数据库 使用Navicat for mysql创建数据库(使用其他工具或直接使用命令行暂不介绍) 2. 2.创建表 双击打

  • C语言中的指针 初阶

    目录 1.指针是什么 2.指针和指针类型 3.野指针 3.1野指针成因 3.2如何规避野指针 4.指针的运算 4.1指针±整数 4.2指针-指针 4.3指针的关系运算 5.指针和数组 6.二级指针 7.指针数组 1.指针是什么 初学者都有一个疑问,那就是指针是什么?简单的说,就是通过它能找到以它为地址的内存单元. 地址指向了一个确定的内存空间,所以地址形象的被称为指针. int main() { int a = 10; int* pa = &a; return 0; } //pa是用来存放地址(

  • C语言中的初阶指针详解

    目录 1.指针是什么 2.指针和指针类型 3.野指针 3.1野指针成因 3.2如何规避野指针 4.指针的运算 4.1指针±整数 4.2指针-指针 4.3指针的关系运算 5.指针和数组 6.二级指针 7.指针数组 ​ 总结 1.指针是什么 ​ 初学者都有一个疑问,那就是指针是什么?简单的说,就是通过它能找到以它为地址的内存单元. 地址指向了一个确定的内存空间,所以地址形象的被称为指针. int main() { int a = 10; int* pa = &a; return 0; } //pa是

随机推荐