C++篇(10)模版进阶

一、非类型模版参数

在讲解该概念之前,我们先来思考下面这个问题。假如我们现在想要实现一个静态的栈结构,大小为10,按照之前所学的知识的话应该这么写:

#define N 10

template<class T>
class Stack
{
private:
	T a[N];
	int _top;
	int _capacity;
};

如果我们要开的空间大小为100的话,只需要把#define定义的宏的10改为100即可。看似程序也很灵活,但是解决不了下面这个问题:加入我们现在要开两个静态栈,一个栈的大小为10,另一个大小为100,那么这个程序就不能同时满足上面的两个需求。所以这时候就需要用非类型模版参数来解决这个问题。

那么什么是非类型模版参数呢?首先,模版参数可以分为类型形参非类型形参。类型形参指出现在模版参数列表中,跟在class或者typename之类的参数类型名称(比如上面代码中的形参T)。而非类型形参就是用一个常量作为类/函数模版的一个参数,在类/函数模版中可将该参数当成常量来使用。

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

template<class T, size_t N>
class Stack
{
private:
	T a[N];
	int _top;
	int _capacity;
};

int main()
{
	Stack<int, 10> st1; //10
	Stack<int, 100> st2; //100

	return 0;
}

注意:这里只能传常量,不能传变量。像下面这段代码就是错误的!!!

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

template<class T, size_t N>
class Stack
{
private:
	T a[N];
	int _top;
	int _capacity;
};

int main()
{
	int x;
	cin >> x;
	Stack<int, x> st3; // error

	return 0;
}

在C++入门中,我们讲过函数参数可以传缺省值,同理,这里也可以给缺省值,并且规则是一样的,都是从右往左连续给缺省值。

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

template<class T = int, size_t N = 1000>
class Stack
{
private:
	T a[N];
	int _top;
	int _capacity;
};

int main()
{
	Stack<int, 10> st1; //10
	Stack<int, 100> st2; //100
	Stack<int> st3;  //1000
	Stack<> st4; //1000
	
	return 0;
}

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

二、模板的特化

2.1 概念

我们先来看下面这段代码:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

template<class T>
bool Less(T x, T y)
{
	return x < y;
}

int main()
{
	cout << Less(1, 2) << endl;

	double* p1 = new double(1.1);
	double* p2 = new double(2.2);
	cout << Less(p1, p2) << endl;

	string* p3 = new string("111");
	string* p4 = new string("222");
	cout << Less(p3, p4) << endl;

	return 0;
}

接着我们运行三次这段代码:

可以发现,三次运行结果各不相同。因为我们这里比较的是指针的大小,而在堆上申请的空间是随机的,所以指针的大小就是随机的,因此出现的结果才会不同。所以这里要进行特殊化处理,也就是在原模板的基础上,针对特殊类型所进行的特殊化的实现方式。模板特化分为函数模板特化类模板特化

2.2 函数模板特化

函数模板的特化步骤:

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

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

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

根据以上步骤,刚才的代码就可以特化成下面的形式:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

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

//特化
template<>
bool Less<double*>(double* x, double* y)
{
	return *x < *y;
}

template<>
bool Less<string*>(string* x, string* y)
{
	return *x < *y;
}

int main()
{
	cout << Less(1, 2) << endl;

	double* p1 = new double(1.1);
	double* p2 = new double(2.2);
	cout << Less(p1, p2) << endl;

	string* p3 = new string("111");
	string* p4 = new string("222");
	cout << Less(p3, p4) << endl;

	return 0;
}

或许有人一开始就会有疑问,为什么原函数模板用的是传值传参?按理来说,在C++中,传引用传参的效率更高啊,不应该写成 const T& 更好吗?是这样的没错,但是如果真的这么写,程序反而出现问题了(如下图所示)。

那为什么特化不上呢?在原函数模板中,const修饰的是x和y本身,但是在特化的函数模板中,const修饰的并不是指针x和y本身,而是修饰的指针所指向的内容,因此这里要把const放在*的右边。程序如下:

template<class T>
bool Less(const T& x, const T& y)
{
	return x < y;
}

//特化
template<>
bool Less<double*>(double* const& x, double* const& y)
{
	return *x < *y;
}

2.3 类模板特化

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

2.3.2 偏特化

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

·部分特化:将模板参数列表中的一部分参数特化

template<class T1,class T2>
class Data
{
public:
	Data()
	{
		cout << "Data<T1, T2>" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};

//偏特化
//特化部分参数
template<class T1>
class Data<T1, char>
{
public:
	Data()
	{
		cout << "Data<T1, char>" << endl;
	}
};

·对参数的更进一步限制

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;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值