【C++】模板+模板特化

这篇具有很好参考价值的文章主要介绍了【C++】模板+模板特化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


铁汁们,今天给大家分享一篇模板+模板特化,来吧,开造⛳️

泛型编程

定义:编写跟具体类型无关的通用代码,是实现代码复用的一种手段。模板是泛型编程的基础。

  • 问:如何实现一个通用的swap函数?
  • 答:写成函数模板,不能在函数重载了。原因:代码复用率低,重载的函数仅是类型不同,若出现了其他新的类型,代码的可维护性降低,若函数中有一处出错了,可能会导致所有的重载函数均出错。

模板

💡模板的本质:本来应该要由我自己写的多份类似代码(除类型不同,代码功能、逻辑相同),现在不需要我自己去写了,我只需要提供一个模板,编译器根据我的实例化,帮我写出代码。

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

函数模板

定义

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

  • 💡Tips : typename是用来定义模板参数的关键字,也可以使用class,但不能用struct代替class。

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;

namespace zzx {
    //函数模板
	template<class T>
	void swap(T& left, T& right)
	{
		T& tmp = left;
		left = right;
		right = tmp;
	}
}

int main()
{
	int a = 3;
	int b = 4;

	cout <<"交换前:" << a << ' ' << b << endl;

    zzx:swap(a, b);

	cout << "交换后:" << a << ' ' << b << endl;
	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

原理分析

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  • 函数模板是蓝图,它本身不是函数,是编译器用特定的方法产生具体类型函数的模具。即:模板其实就是本来应该由我们自己做的重复的事情交给了编译器去做。

  • 编译阶段,编译器通过实参的类型推演生成相对应类型的函数。eg:当用double类型的参数使用函数模板时,编译器根据实参的类型推演出模板参数T的类型,将T确定为double类型,产生一份专门处理double类型的代码。

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;

namespace zzx {
	template<class T>
	T Add(T& left, T& right)
	{
		return left + right;
	}

	double Add(double& left, double& right)
	{
		return left + right;
	}

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

int main()
{
	int a = 1;
	int b = 2;
	cout << zzx::Add(a, b) << endl;

	double c = 1.1;
	double d = 2.2;
	cout << zzx::Add(c, d) << endl;

	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法
【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

函数模板的实例化

定义:用不同类型参数使用函数模板时,称为函数模板的实例化。模板参数的实例化有两种,分别为隐式实例化和显示实例化。

  1. 隐式实例化:也称为推演实例化,编译器根据实参的类型,推演出模板参数的实际类型。
    【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  2. 显示实例化:在函数名后加<>来指定模板参数的实际类型。

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;

namespace zzx {

	template<class T>
	T Add(const T& left, const T& right)
	{
		return left + right;
	}
}

int main()
{
	cout << zzx::Add<int>(1, 2.2) << endl;

	cout << zzx::Add<double>(1, 2.2) << endl;

	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  • 应用场景一:函数参数无模板参数,在函数体内有模板参数 -》只能显示实例化,不能隐式实例化,因为无法通过实参的类型推演出模板参数的实际类型,需要指定模板参数的类型。
template<class T1, class T2>
void fun(int n)
{
	T1 a ;
	T2 b;
}

int main()
{
	fun<int, double>(10);

	return 0;
}
  • 应用场景一:对于stack类,若想让栈1存int类型的数据,让栈2存double类型的数据。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;

namespace zzx{
	template<class T>
	class stack {
		T* a;
		size_t top;
		size_t capacity;
	};
}


int main()
{
	zzx::stack<int> s1;

	zzx::stack<double> s2;
	return 0;
}

模板参数的匹配原则

  • 有现成的就吃现成,不要自己动手做 -》非模板函数和同名的模板函数同时存在时,且其他条件相同的情况下,会优先调用非模板函数。
    【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法
  • 没有现成的,有更合适的就吃更合适的,哪怕要自己动手做 。-》无非模板函数,但与同名的模板函数参数类型匹配,就去调用模板函数。
    【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法
  • 没有现成的,也没更合适的,就将就吃 -》无非模板函数,与同名的模板函数参数类型不匹配,但普通函数允许隐式类型转化,就去调用普通函数。
    【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法
    💡Tips : 模板函数不允许隐式类型自动转化,但普通函数支持隐式类型自动转化。

类模板

定义

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  • 💡Tips : 类模板后面一定要加分号( ; ) 。

类模板的实例化

类模板只能显示实例化 。类名+<>指定模板参数的实际类型。

  • 显式实例化的类型不同,它们就是不同类。

  • 普通类,类名就是整个类的类型。对于类模板,类名就不是类型,类名+<数据类型>才是整个类的类型。

  • 类模板中的函数要在外面进行定义时,即:声明和定义分离。声明和定义要放在同一文件中,且需要在函数名前加上类名<数据类型>: : 。

非类型模板参数

  1. 模板参数可以分为类型形参和非类型形参。

  2. 类型模板参数:就是普通的类的模板参数,模板参数声明在template 中,T是模板参数。

  3. 非类型模板参数是指在使用模板时传入整形常量,用一个常量作为类(函数)模板的一个参数,该常量只能为整形常量,在编译期间就可以确定其值。用于指定模板的一些具体细节,如数组的长度或函数的输入参数类型。

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<array>

using namespace std;

namespace zzx{
	//非类型模板参数,只能是整形常量
	template<class T, size_t N = 10>
	class array {  

	private:
		T a[N];  //定义一个静态数组
		size_t size;
	};
}


int main()
{
	//array为STL中的一个容器,底层为静态数组,不会初始化,只会在越界的时候进行检查报错
	zzx::array<int, 5> _a1;

	std::array<int, 6> _a2;
	_a2[5];

	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  • array为STL中的一个容器,底层为静态数组,不会初始化,只会在越界的时候进行检查,程序会崩溃。

模板特化

定义

定义:在原模板类(函数)的基础上,针对特殊类型进行特殊化处理的实现方式。模板特化划分为函数模板的特化和类模板的特化。

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

函数模板特化

1.函数模板特化的步骤

  • 必须先需要一个原函数模板。

  • 关键字template 后+ <>,函数名后+<特化的类型>。

  • 函数的形参表的格式、函数内部的实现逻辑必须要与原函数模板参数格式、内部实现逻辑一致,但如果模板参数的类型为const T&,T为Date*,此时特化后的函数模板参数的类型为T,防止类型转换以及常引用导致结果出错。

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

	friend ostream& operator<<(ostream& _cout, const Date& d);
private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

template<class T>  //函数模板
bool Less (const T& x, const T& y)
{
	return x < y;
}

int main()
{
	Date d1(2022, 7, 8);
	Date d2(2022, 7, 10);

	cout << Less(d1, d2) << endl;  //结果正确

	//按地址比较了,没有按指针指向的内容去比较
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl;  //结果错误

	Date* p3 = new Date(2022, 7, 8); //连续两次new出来的地址大小无大小关系,new在堆上随意取一块地址
	Date* p4 = new Date(2022, 7, 10);
	cout << Less(p3, p4) << endl;   //结果错误

	return 0;

}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

template<class T>  //函数模板
bool Less(T& x, T& y)
{
	return x < y;
}

//template<>  //对函数模板进行特化
//bool Less<Date*>(Date* x, Date* y)
//{
//	return *x < *y;
//}


bool Less(Date* x, Date* y) //普通函数,专门用来处理Date*的比较
{
	return *x < *y;
}
  • Less(p1, p2)结果错误,是因为此处去调用Less函数模板,编译器根据实参的类型推演出T的类型为Date*,在Less内部是按照指针的值(地址)比较的,而不是按照指针指向的内容比较的。
  • 一般不建议对函数模板进行特化,而是直接将该函数实现给出,代码的可读性高。

类模板特化

  1. 全特化:模板参数列表中所有参数的类型全部都是确定的类型。
//全特化
template<>
class AA<char, int>
{
public:
	AA() { cout << "AA<char, int>" << endl; }
private:
	char _d1;
	int _d2;
};


int main()
{
    AA<char, int> a2;

	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  1. 偏特化有两种,一种是特化部分参数,另一种是对参数类型进行条件限制,如:将参数类型偏特化为指针或者是引用类型等。
template<class T1>
class AA<T1, int>
{
public:
	AA() { cout << "AA<T1, int>" << endl; }
private:
	T1 _d1;
	int _d2;
};

int main()
{
    AA<char, int> a2;

	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

//偏特化2:对参数类型进行条件限制
template<class T1, class T2> 
class AA<T1*, T2*>  //两个参数偏特化为指针类型
{ 
public:
	AA() { cout << "AA<T1*, T2*>" << endl; }
private:
	T1 _d1;
	int _d2;
};

template<class T1, class T2>
class AA<T1&, T2&>  //两个参数偏特化为引用类型
{
public:
	AA() { cout << "AA<T1&, T2&>" << endl; }
private:
	T1 _d1;
	int _d2;
};

int main()
{ 
    AA<int*, char*> a1;

	AA<int&, char&> a2;

	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  1. 💡匹配顺序:全特化 > 偏特化 > 普通类模板。
//类模板
template<class T1, class T2>
class AA
{
public:
	AA() { cout << "AA<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

//全特化
template<>
class AA<char, int>
{
public:
	AA() { cout << "AA<char, int>" << endl; }
private:
	char _d1;
	int _d2;
};

//偏特化
template<class T1>
class AA<T1, int>
{
public:
	AA() { cout << "AA<T1, int>" << endl; }
private:
	T1 _d1;
	int _d2;
};

int main()
{
    
	AA<char, int> a1;  //全特化
	 
	AA<int, int> a2;  //偏特化

	AA<int, char> a3;  //普通类模板
    
    return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

应用实例

  1. 场景一:处理优先队列中T为Date*的数据。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<queue>

using namespace std;

int main()
{
  
    priority_queue<Date*> pq;
	pq.push(new Date(2022, 7, 8));
	pq.push(new Date(2022, 7, 10));
	pq.push(new Date(2022, 7, 6));

	while (!pq.empty())
	{
		cout << *pq.top() << endl;
		pq.pop();
	}

	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  • 原因分析:因为 priority_queue<class T, class Container = vector, class Compare = less>,此处默认去调用仿函数less,编译器根绝实参的类型推演出T为Date*,导致Less内部是按照指针的值(地址)比较的,而不是按照指针指向的内容比较的。

  • 解决方法:手动写个专门处理Date*类型的仿函数 或者 对仿函数类进行特化。

class less {  //小于
	public:
		bool operator()(const Date* x, const Date* y)
		{
			return *x < *y;
		}
	};
namespace zzx {
	template<class T> 
	class less {  //小于
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<>
	class less<Date*>
	{  //小于
	public:
		bool operator()(Date* x, Date* y)
		{
			return *x < *y;
		}
	};
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法
【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

  1. 场景二:处理优先队列中T为任意类型*的数据。采用偏特化的第二种。
namespace zzx {
	template<class T> 
	class less {  //小于
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>  //偏特化2
	class less<T*>
	{  //小于
	public:
		bool operator()(T* x, T* y)
		{
			return *x < *y;
		}
	};
}

int main()
{
	priority_queue<Date*, vector<Date*>, zzx::less<Date*>> pq1;
	pq1.push(new Date(2022, 7, 8));
	pq1.push(new Date(2022, 7, 10));
	pq1.push(new Date(2022, 7, 6));

	while (!pq1.empty())
	{
		cout << *pq1.top() << endl;
		pq1.pop();
	}

	priority_queue<int*, vector<int*>, zzx::less<int*>> pq2;
	pq2.push(new int(1));
	pq2.push(new int(2));
	pq2.push(new int(3));

	while (!pq2.empty())
	{
		cout << *pq2.top() << endl;
		pq2.pop();
	}

	return 0;
}

【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

模板分离编译

原理分析

  1. 分离编译模式:一个项目由若干个文件组成,而每个源文件单独编译生成目标文件,最后将所有的目标文件链接起来形成一个单一的可执行文件的过程称为分离编译模式。

  2. 模板分离编译:模板的声明和定义分离。将模板的声明放在头文件.h文件,把模板的定义放在源文件.cpp文件。
    【C++】模板+模板特化,c++,开发语言,数据结构,算法,学习方法

解决方法

  1. 在模板的定义位置处显示实例化,进行类型声明,但这种方法不实用。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

using namespace std;

#include"Add.h"

namespace zzx{

	//显示实例化
	template
	int Add<int>(const int&, const int&);

	template
	double Add<double>(const double&, const double&);


	template<class T>
	T Add(const T& left, const T& right)
	{
		return left + right;
	}
}
  1. 将模板的定义和声明放在同一个文件中,将该文件定义为“.hpp"或者“.h"。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>

using namespace std;

namespace zzx {

	template<class T>
	T Add(const T& left, const T& right);

	template<class T>
	T Add(const T& left, const T& right)
	{
		return left + right;
	}
}

模板总结

  • 优点:模板复用了代码,节省了资源。增强了代码的灵活性。

  • 缺点:会导致代码膨胀,也会导致编译时间变长。不易定位错误(解决方法:排除法,一段一段注释,写一部分就编译一部分。

💡Tips:本质:本来应该由我们自己去写的多份类似代码,我们只需要提供一个模具,让编译器根据实例化帮我们去写。

铁铁们,模板+模板特化就到此结束啦,若博主有不好的地方,请指正,欢迎铁铁们留言,请动动你们的手给作者点个👍鼓励吧,你们的鼓励就是我的动力✨文章来源地址https://www.toymoban.com/news/detail-842865.html

到了这里,关于【C++】模板+模板特化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】模板进阶——非类型模板参数、模板特化、模板分离编译

    模板参数分为类型形参 与 非类型形参 类型形参 :出现在模板参数列表中,跟在class或者typedename之类的参数类型名称。 非类型形参 :用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。 非类型模板参数的优势: 有些容器需要在创建对象

    2024年02月01日
    浏览(49)
  • C++类模板的特化(三)

    本文主要介绍类模板的特化、局部特化和缺省模板实参; 类模板的特化(Class Template Specialization)是指为特定的模板参数提供自定义实现的过程。通过特化,我们可以针对某些特定的类型或条件提供不同的行为或实现; 如果需要特化一个类模板,需要特化该模板中的所有成员

    2024年02月11日
    浏览(44)
  • [C++] 模板进阶(非类型模板参数,特化,分离编译)

    模板参数分类类型形参与非类型形参。 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。 我们举例来看一下: 注意: 1. 浮点数、类对象

    2024年02月04日
    浏览(34)
  • 【C++】模板进阶--非类型模板参数&&模板特化及分离编译

    模板参数分为 类型形参 与 非类型形参 ,其中,类型形参即出现在模板参数列表中,跟在class或者typename之类的参数类型名称,非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用 我们以定义一个静态的数组为例,在没有非

    2023年04月23日
    浏览(44)
  • 【C++】模板进阶—非类型模板参数、模板特化及模板的分离编译

    🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。 🚁 个人主页:不 良 🔥 系列专栏:🛸C++  🛹Linux 📕 学习格言:博观而约取,厚积而薄发 🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与诸君一同

    2024年02月16日
    浏览(36)
  • 【C++学习】模板进阶——非类型模板参数 | 模板的特化 | 分离编译

    🐱作者:一只大喵咪1201 🐱专栏:《C++学习》 🔥格言: 你只管努力,剩下的交给时间! 模板我们之前一直都在使用,尤其是在模拟STL容器的时候,可以说,模板给类增加了更多的可能性,是C++最重要的部分之一。下面本喵来更深入的讲解一下模板。 在上面代码中,创建了

    2023年04月13日
    浏览(35)
  • 【C++初阶(十)】C++模板(进阶) ---非类型模板参数、模板的特化以及模板的分离编译

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 模板参数可分为类型形参和非类型形参。 类型形

    2024年01月18日
    浏览(36)
  • 【C++】数据结构与算法:常用排序算法

    😏 ★,° :.☆( ̄▽ ̄)/$: .°★ 😏 这篇文章主要介绍常用排序算法。 学其所用,用其所学。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不迷路🥞 排序算法是计算机科学中常见的一类算法,用于将一组数据按照特定的顺序进行排

    2024年02月14日
    浏览(48)
  • 【数据结构与算法C++实现】3、排序算法

    原视频为左程云的B站教学 外层循环 :n个数需要冒n-1个泡上去,剩下的一个必然是最小的。所以外层循环执行n-1轮 内层循环 :比大小,第1个泡需要比n-1次,第2个泡,比较n-2次… 选择: 每次从待排序序列中选择 最小的一个 放在已排序序列的后一个位置 原理类似于对扑克牌

    2024年02月11日
    浏览(56)
  • C语言数据结构与算法

    冒泡排序 例题 顺序表下的 冒泡排序 注意:冒泡排序 稳定,最多执行n(n-1)/2次 选择排序不稳定,平均比较次数n(n-1)/2 直接插入排序,是在有序基础上,速度最快且稳定的排序方法。 希尔排序是 不稳定的 顺序查找 二分查找(非递归) 二分查找(递归) 数组 链表 查询 快 慢

    2024年02月06日
    浏览(70)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包