模板的进阶

目录

非类型模板参数

C++11的静态数组容器-array 

按需实例化

模板的特化

函数模板特化

类模板特化

全特化与偏特化

模板的分离编译

总结


非类型模板参数

基本概念:模板参数类型分为类类型模板参数和非类类型模板参数

  • 类类型模板参数:跟在class 或 typename之后的形参
  • 非类类类型模板参数:用一个常量作为类模板的参数

功能:编译时合理分配大小

#include <iostream>
using namespace std;

namespace bit
{
	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;
	};
}

int main()
{
	bit::array<int> a1;
	bit::array<int, 10> a2;
	bit::array<int, 100> a3;
	return 0;
}

注意事项: 

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

2、非类类型模板参数是在编译时传参,函数参数是在运行时传参

3、函数参数(T 对象1,T 对象2)| 模板参数<class 类型1,class 类型2>

C++11的静态数组容器-array 

array文档:<array> - C++ Reference (cplusplus.com)

#include <iostream>
#include <assert.h>
#include <array>
using namespace std;

int main()
{
	std::array<int, 10> a1;
	int a2[10];

	//越界读,检查不出来
	a2[10];

	//越界写,抽查,局限多,很多位置查不出来(x86环境下运行a2[15]不报错,a[10] = 1报错)
	a2[15] = 1;

	//任意读写越界都能检查出来
	a1[10];//报错

	return 0;
}

优点: 可以避免数组越界问题(但实际上这些内容vector就可以做到,array没啥用)

std::vector<int> v1(10,0); 
v1[10]//v1[10]也可以检测出

缺点:会出现栈溢出问题

std::array<int,1000000> a3;//报错
std::vector<int> v2(1000000,0);//正确

按需实例化

include <iostream>
#include <array>
#include <vector>
#include <assert.h>
using namespace std;

namespace bit
{
	template<class T, size_t N = 10>
	class array
	{
	public:
		T& operator[](size_t index)
		{
			assert(index < N);
			
			size(1);//语法错误,但是不报错

			return _array[index];
		}

		const T& operator[](size_t index)const
		{
			assert(index < N);
			return _array[index];
		}
		
		size_t size()const
		{
			return _size;
		}

		bool empty()const
		{
			return 0 == _size;
		}
	private:
		T _array[N];
		size_t _size;
	};
}

int main()
{

	bit::array<int> a1;
    cout<<a1.empty()<<endl;//不报错
    a1[1];//增加一个调用a1[1]时报错
	return 0;
}

运行上述代码后不会报错,即使size(1)是一个语法错误(size函数不需要传参)是因为

  1. 在编译器遇到模板时,预处理阶段后不会直接编译,而是根据模板的“蓝图” + 传入模板的参数类型等内容,将模板进行实例化后才会进行编译阶段(调试时是已经经历了四个阶段的)
  2. 在实例化类模板时会进行按需实例化,即调用哪个成员函数就实例化哪个(调用empty就仅实例化该函数)

没有报错的本质就是没有调用operator[],所以operator[]中的错误不会被检查出来,只有调用才会细致检查语法错误

  • 当然编译器还是会大体框架进行检查,比如少了}多个}之类的错误

模板的特化

基本概念:在原模板类的基础上,针对特殊类型所进行特殊化处理

        通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到

一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板:

#include <iostream>
using namespace std;

class Date
{
public:
	friend ostream& operator<<(ostream& _cout, const Date& d);

	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);
	}
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(T left, T right)
{
    return left < right;
}

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

    Date d1(2022, 7, 7);
    Date d2(2022, 7, 6);
    cout << Less(d1, d2) << endl; // 可以比较,结果正确(会调用日期类对象的<重载)

    Date* p1 = &d1;
    Date* p2 = &d2;//(传入日期类对象的指针进行比较)
    cout << Less(p1, p2) << endl; // 可以比较,结果错误

    return 0;
}

        p1指向的d1显然大于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,

而比较的是p1和p2指针的地址,因而无法达到预期而错误

函数模板特化

注意事项:

  • 1、必须要现有一个基础的函数模板
  • 2、template后接一个空的尖括号<>
  • 3、函数名后跟一对尖括号,尖括号中指定需要特化的类型
  • 4、函数形参顺序必须要和模板函数的基础参数类型完全相同,若不同则编译器可能报错
#include <iostream>
using namespace std;


class Date
{
public:
	friend ostream& operator<<(ostream& _cout, const Date& d);

	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);
	}
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(T left, T right)
{
	cout << "bool Less(T left, T right)" << endl;

	return left < right;
}


特化(但是该特化还是会出错)
//template<>
//bool Less(Date* left, Date* right)
//{
//	cout << "bool Less(T* left, T* right)" << endl;
//	return *left < *right;
//}

//不会出错但是不符合特化定义
template<class T>
bool Less(T* left, T* right)
{
	cout << "bool Less(T* left, T* right)" << endl;
	return *left < *right;
}


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

	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确

	Date* p1 = new Date(2022, 7, 7);
	Date* p2 = new Date(2022, 7, 8);
	cout << Less(p1, p2) << endl; // 可以比较,结果错误

	int* p3 = new int(3);
	int* p4 = new int(4);
	cout << Less(p3, p4) << endl; //可以比较,结果错误

	return 0;
}

~~依然是哪个类型更匹配就用哪个 ~~

结论:函数模板不建议特化do且函数模板特化的使用场景少

类模板特化

全特化与偏特化

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

偏/半特化:将模板参数列表中的部分参数确定化

  • 偏特化又分为:“部分特化” 和“参数更进一步的限制”
#include <iostream>
using namespace std;


//Data类模板
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;
	}
};

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

//偏/半特化(对参数进一步限制)
template<class T1,class T2>
class Data<T1*, T2*>
{
public:
	Data()
	{
		cout << "Data <T1*,T2*>" << endl;
	}
};

//偏/半特化(对参数进一步限制)
template<class T1, class T2>
class Data<T1&, T2*>
{
public:
	Data()
	{
		cout << "Data <T1&,T2*>" << endl;
	}
};

int main()
{
	Data<int, int> d1;
	Data<int, char> d2;
	Data<char, char> d3;
	Data<char*, char*> d4;
	Data<int*, char*> d5;
	Data<int*, string*> d6;
	Data<int&, string*> d7;
	return 0;
}

匹配机制:有现成的就用现成的,没有现成的就用那个最合适的

注意事项:如果传入的是Data*,特化时的T*就会变成Date*而不是Data**,T*是一个整体

模板的分离编译

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

假如有以下场景,模板声明和定义分离,在头文件中进行声明,在源文件中进行定义:

//a.h文件
tempate<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;
}

 22、2:33处

注意事项:模板的声明和定义支持分离,但不支持分离在两个文件(STL库中的模板的定义和分离都是在.h文件中的)

总结

优点:

  1. 复用了代码,节省资源,更快的迭代开发,C++的标准模板库也因此而生
  2. 增强代码灵活性

缺点:

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息十分混乱,不易定位

~over~

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Thymeleaf进阶方面,有一些资源可以帮助您深入学习和掌握Thymeleaf的高级功能和技巧。首先,您可以参考Thymeleaf的GitHub主页,其中提供了该项目的源代码和文档,了解最新版本的功能和更新。此外,您还可以查阅一些专门关于Thymeleaf的教程和指南,这些资源可以帮助您更好地理解Thymeleaf的用法和特性。 对于Thymeleaf的字符串操作,您可以参考一些教程和博客文章,其中会介绍在Thymeleaf下常用的字符串操作。这些操作可以帮助您在生成HTML时,处理和调整字符串的内容,以及实现一些特定的需求。 另外,Thymeleaf也是基于Web标准的,特别是HTML5。它允许您创建完全符合验证规范的模板,并且在Spring Boot中,官方推荐使用Thymeleaf来替代JSP。这意味着Thymeleaf可以帮助您更好地构建和管理Web应用程序的前端部分,并且与后端的数据交互更加方便和灵活。 总结起来,在Thymeleaf的进阶学习中,您可以参考Thymeleaf的GitHub主页、教程和指南,以及探索其在Web开发中的优势和特点。这些资源将帮助您更好地理解和应用Thymeleaf的高级功能,并提升您在使用Thymeleaf进行开发时的效率和技术水平。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Thymeleaf模板的学习一【基础知识】【java进阶编程】](https://blog.csdn.net/notlikeregist/article/details/90171063)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [thymeleaf进阶使用](https://blog.csdn.net/whatlookingfor/article/details/78353819)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值