模板的进阶

发布于:2025-07-20 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

1、关于前缀class/typename

2、非类型模板参数

3、模板的特化

1)函数模板的特化

2)类模板的特化

3)应用

4、模板分离编译

5、总结


模板是一种泛型编程,编译器可以根据情况将使用模板的部分实例化为具体的一些内容,方便程序员使用,这样一来,程序员只需要写一份代码,其底层可以根据情况需要,生成多份代码来进行使用。

1、关于前缀class/typename

在使用模板的过程中,我们往往可以使用template<class T>的形式来进行实现,其中class也可以和typename相互替代使用,二者区别不大,但是有些地方还是需要特别地去进行使用

template<class container>
void Print(const container& v)
{
	container::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
}

class A
{
public:
	int begin()
	{
		return 0;
	}
	static int const_iterator;
private:
	int _a;
	
};
int A::const_iterator = 1;

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);

	Print(v);

	A(a);
	A::const_iterator  = a.begin();//使用静态成员的代码与使用迭代器的代码很相似
}

这段代码无法正常运行,问题出在Print函数的迭代器,这里在定义迭代器时必须加上typename作为前缀,当我们使用静态成员时的代码与这里的代码很像,所以如果不加,那么就会产生歧义,编译器并不明确container::const_iterator是类型还是对象,无法编译通过。不过,编译器虽然提示必须加上typename的前缀,但经过尝试后,发现如果加上class作为前缀也没有什么问题。

2、非类型模板参数

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

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

非类型形参:用一个常量作为类模板或者函数模板的一个参数,在类模板或者函数模板中可将该参数当成常量来使用

template<class T,size_t N>
class Stack
{
private:
	T _a[N];
	int top;
};
int main()
{
	Stack<int, 10> st1;
	Stack<int, 100> st2;
}

注意:非类型模板参数被认为是常量(不能被修改),且必须为整型

3、模板的特化

通常情况下,使用模板可以实现一些与类型无关的代码,但是对于一些特殊类型可能会得到一些错误的结果,需要去进行特殊处理,比如在使用仿函数实现大小比较时,如果需要对两个指针指向的内容进行比较,那么使用仿函数传参时,只会传入两个指针指向的地址,并不能对指针内容进行解引用实现我们需要的比较,因此我们需要对模板进行特化,也就是在原模板类的基础上,针对特殊类型所进行特殊化的实现方式

注意:特化的模板前面必须要有一个基础的模板

1)函数模板的特化

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


template<>
bool Less<int*>(int* left, int* right)
{
	return *left < *right;
}


bool Less(int* left, int* right)
{
	return *left < *right;
}


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

2)类模板的特化

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

template<class T1,class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<T1,T2>" << endl;
	}
private:
	int d1;
	char d2;
};

template<>
class Date<int, char>
{
public:
	Date()
	{
		cout << "Date<int,char>" << endl;
	}
private:
	int d1;
	char d2;
};

偏特化:将模板参数类表中的一部分参数特化,或者是对模板参数更进一步的条件限制设计出一个特化版本

template<class T1,class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<T1,T2>" << endl;
	}
private:
	int d1;
	char d2;
};


template<class T1>
class Date<T1,char>
{
public:
	Date()
	{
		cout << "Date<T1,char>" << endl;
	}
private:
	int d1;
	char d2;
};


template<class T1, class T2>
class Date<T1*,T2*>
{
public:
	Date()
	{
		cout << "Date<T1*,T2*>" << endl;
	}
private:
	int d1;
	char d2;
};


template<class T1, class T2>
class Date<T1&,T2&>
{
public:
	Date()
	{
		cout << "Date<T1,T2>" << endl;
	}
private:
	int d1;
	char d2;
};

3)应用

可以使用模板特化来对之前的日期类进行比较大小并排序

注意:模板特化前需要先有基础模板、对日期类的定义必须放在前面

class Date
{
	friend ostream& operator<<(ostream& out, Date& d);
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)
	{
		if (_year < d._year)
			return true;
		if (_year == d._year && _month < d._month)
			return true;
		if (_year == d._year && _month == d._month &&_day< d._day)
			return true;
		return false;
	}
	
private:
	int _year;
	int _month;
	int _day;
};

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

template<class T>
struct LessPDate
{
	bool operator()(T* left, T* right)
	{
		return *left < *right;
	}
};
template<>
struct LessPDate<Date*>  // Date类需要再前面定义
{
	bool operator()(Date* left, Date* right)
	{
		return *left < *right;
		//return (*left).operator<(*right);
	}
};

int main()
{
	Date d1(2025, 8, 19);
	Date d2(2025, 7, 19);
	Date d3(2025, 9, 19);

	vector<Date> v;
	v.push_back(d1);
	v.push_back(d2);
	v.push_back(d3);

	vector<Date*> v1;
	v1.push_back(&d1);
	v1.push_back(&d2);
	v1.push_back(&d3);

	sort(v1.begin(), v1.end(), LessPDate<Date*>());

	//for (auto& e : v)
	//{
	//	cout << e << endl;
	//}
	cout << **v1.begin() << endl;
}

4、模板分离编译

将模板函数的声明和定义分离,就像之前日期类的函数声明和定义分离一样,将模板声明放在.h文件中,定义放在.cpp文件中,在.cpp文件中编译器无法看到模板函数的实例化,也就是说,由于与模板声明分离,在定义的部分就不明确模板具体代表什么内容,因此不会生成具体的函数。

那么可以使用将声明和定义放在一个文件内的办法来进行解决,或者在定义的.cpp文件中显式实例化模板

5、总结

模板的优点在于其复用了代码,节省资源能够实现更快的迭代开发,C++的标准模板库(STL)因此而产生,同时也增强了代码的灵活性

缺点在于模板会导致代码膨胀问题,也会导致编译的时间变长,并且,在出现模板编译错误时,错误信息非常凌乱,不容易定位错误


网站公告

今日签到

点亮在社区的每一天
去签到