C++模板进阶

一、非类型模板参数

模板参数分为:类型形参、非类型形参

类型形参:class与typename都是参数类型

非类型形参:用一个常量作为模板的一个参数,一般是整型常量

#include<iostream>
#include<array>
using namespace std;

//class T为类型模板参数,用于适配各种类型
//size_ t N为非类型模板参数,只能为整型常量
template<class T,size_t N>//下面的size_t N虽然没有加const但默认为常量
class Array
{
public:
private:
	T _a[N];
};



template<class T,size_t N=10>
//template<class T,double N=10>//会报错,类型非法
void func(const T& a)
{
	N = 20;//会报错必须为可操作得左值,说明N非类型模板参数为常量
}



int main()
{
	//非类型模板参数可以对不同得对象开辟不同大小的空间
	Array<int,10> a1;
	Array<double,20> a2;

	int arr[15];
	array<float, 15> a3;//C++11中更新的容器,对标C语言中的静态数组
	//不提供初始化,但是有严格的越界检查,读写全面检查

}

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

2.非类型模板参数必须在编译器就能确认

二、模板特化

模板对于一些特殊类型可能会产生一些错误的结果

就比如下面实现的比较大小的代码,对于Date类的特定对象可以实现对比功能,但是如果使用特定对象的指针进行比较,就会产生错误

因此需要使用模板进行特化,也就是在原来模板的基础上,对特殊类型进行特殊化处理

#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)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

private:
	int _year;
	int _month;
	int _day;
};


template<class T>
bool Less(T left, T right)
{
	return left < right;
}

int main()
{
	cout << Less(1, 5) << endl;//1	可以比较,结果正确

	Date d1(2023, 5, 10);
	Date d2(2023, 5, 1);
	cout << Less(d1, d2) << endl;//0	可以比较,结果正确

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl;//1	可以比较,结果错误
	return 0;
}

可以参数Less函数一般都可以进行正常比较,但是以如上代码为例,进行自定义对象的对比正常,但是对于指向自定义类型对象的指针对比错误。因此需要特化

1.函数模板特化

函数模板的特化步骤:

1. 必须要先有一个基础的函数模板

2. 关键字template后面接一对空的尖括号<>

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

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

函数模板特化使用较少,因为可以直接给出特定的函数用于实现,也就是函数重载

#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)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

private:
	int _year;
	int _month;
	int _day;
};


template<class T>
bool Less(T left, T right)
{
	return left < right;
}

//函数模板特化--对某些类型进行特殊化处理
template<>
bool Less<Date*>(Date* left, Date* right)//全特化
{
	return *left < *right;
}

//实例化重载
bool Less(Date* left, Date* right)
{
	return *left < *right;
}



void test_1()
{
	cout << "Less" << endl;
	cout << Less(1, 2) << endl;

	Date d1(2023, 5, 10);
	Date d2(2023, 5, 5);
	cout << Less(d1, d2) << endl;

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl;//调用特化之后的版本,不通过模板生成
}



int main()
{
	test_1();
	return 0;
}

2.类模板特化

类模板特化分为全特化偏特化

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

偏特化:对模板参数进行限制的特化版本

全特化可以理解为所有参数都锁定的特化,偏特化可以理解为部分参数特化

 

#include<iostream>
using namespace std;

//类模板
template<class T1,class T2>
class modle
{
public:
	modle()
	{
		cout << "modle<T1,T2>" << endl;
	}
private:
	T1 _m1;
	T2 _m2;
};

//类模板全特化
template<>
class modle<int,char>
{
public:
	modle()
	{
		cout << "modle<T1,T2>" << endl;
	}
private:
	int _m1;
	char _m2;
};

//偏特化
template<class T1>
class modle<T1, int>//将模板参数列表的一部分参数特化,将第二个参数转化为int
{
public:
	modle()
	{
		cout << "modle<T1,int>" << endl;
	}
private:
	T1 _m1;
	int _m2;
};

//将两个参数偏特化为指针类型
template<typename T1,typename T2>
class modle<T1*, T2*>
{
public:
	modle()
	{
		cout << "modle<T1*,T2*>" << endl;
	}
private:
	T1 m1;
	T2 m2;
};

//将两个参数偏特化为引用类型
template<typename T1, typename T2>
class modle<T1&, T2&>
{
public:
	modle(const T1& m1,const T2& m2)
		:_m1(m1),_m2(m2)
	{
		cout << "modle<T1&,T2&>" << endl;
	}
private:
	const T1& _m1;
	const T2& _m2;
};


void test_2()
{
	modle<int, int> m1;//偏特化的部分参数特化版本
	modle<int,char> m2;//全特化
	modle<int*, int*> m3;//偏特化的指针版本
	modle<int&, int&> m4(1, 5);//偏特化的引用版本
}



int main()
{
	test_2();
	return 0;
}

三、模板的分离编译

一个项目由若干个源文件共同实现,每个源文件会独立线性生成相应的预处理文件(.i),编译文件(.s),汇编文件(.o),最后再将所有目标文件链接,形成单一的可执行文件的过程为分离编译模式

 func.h

#pragma once
#include<iostream>
#include<array>
#include <vector>
using namespace std;

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;
}

// 声明和定义放到一起,直接就可以实例化,编译时就有地址,不需要链接

void func();
//模板不支持声明与定义不在同一个文件
//但是模板支持在一个文件内部,声明和定义分离

#include"func.h"
template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

void func()
{
	cout << " void func()" << endl;
}

//显式实例化,此时编译时就知道需要实例化一个double类型的Add函数
template
double Add<double>(const double& left, const double& right);
//但是这样对于其他类型需要不断得显示实例化,十分麻烦

工程只有再链接的时候才会寻找函数地址,但是函数模板只有在调用的时候才会产生实例化,因此再没有实例化的时候并没有地址,在链接时会产生报错

解决方法:

1.将声明和定义放在同一个文件内,一般都是统一放在头文件内

2.模板定义的位置显示实例化,对于多个不同类型调用十分麻烦,不建议使用

模板总结

优点缺点
模板复用代码,节省资源,使得代码简明高效模板会导致代码膨胀,对于不同的类型进行不同的实例化,会导致编译时间变长
增强的代码的灵活性出现模板编译错误时,错误信息凌乱,不易定位

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值