首页 > 编程语言 >【C++】详细介绍模版进阶,细节满满

【C++】详细介绍模版进阶,细节满满

时间:2024-11-12 14:44:23浏览次数:3  
标签:进阶 模版 函数 C++ 参数 fun 特化 指针

目录

一、非类型模版参数:

1、介绍:

2、使用:

3、注意:

4、应用

二、模版特化

(一)、概念

(二)、函数模版特化

1、步骤:

2、举例:

3、不建议使用函数模版特化

(三)、类模版特化

1.全特化:

2、偏特化

2.1、部分特化

2.2、参数更进一步的限制

2.3、注意:

2.4、普通指针变量传递给const指针变量的引用(权限缩小)

三、模版的分离编译

1、什么是分离编译?

2、模版的分离编译

四、模版的优缺点

1.优点:

2、缺点:


一、非类型模版参数:

1、介绍:

模板参数分类类型形参与非类型形参。 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

2、使用:

#define N 20
template<class T>
struct stack
{

private:
	T _arr[N];
	T _top;
};

int main()
{
	stack<int> s1;
    stack<int> s2;
	return 0;
}

如上述代码,模拟实现一个数组栈时,栈s1想要10个空间,栈s2想要20个空间,此时的结构就无法实现。

此时我们就可以使用非类型模版参数,即增加一个非类型模版参数:

template<class T,size_t N>
struct stack
{

private:
	T _arr[N];
	T _top;
};

int main()
{
	stack<int,10> s1;
	stack<int,20> s2;
	return 0;
}

此时,我们需要多少变量,就传多少值。

3、注意:

(1)、非类型模版参数只能传整形常量,浮点数、类对象以及字符串等等是不允许作为非类型模板参数的。 (2)、非类型的模板参数必须在编译期就能确认结果。 (3)、模版是按需实例化,只实例化调用过的函数,这就导致即使某些模版函数里面有一些错误,但如果没有调用该函数的话,也不会检查出来。 如上,N为常量,即使fun函数里面对N进行++,但没有对象调用fun函数,编译器就不会报错。

4、应用

应用一:位图

应用二:array容器

二、模版特化

(一)、概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理。 例如用仿函数进行大小比较时,如果传入的是指针,我们预期是想比较指针所指向内容的大小,但它会直接比较指针(地址)的大小,这是就需要对这一类型进行特化。

(二)、函数模版特化

1、步骤:

1、必须要先有一个基础的函数模板(正常模版) 2、关键字template后面接一对空的尖括号<> 3、函数名后跟一对尖括号,尖括号中指定需要特化的类型 4、函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

2、举例:

template<class T>
class test
{
public:
	test(T a1):a(a1)
	{}
	bool operator<(test a2)
	{
		return this->a < a2.a;
	}
private:
	int a;
};
//函数特化
template<class T>
bool myLess(T s1, T s2)
{
	cout << "程序走了正常函数模版" << endl;
	return s1 < s2;
}

template<>
bool myLess<test<int>*>(test<int>* s1, test<int>* s2)
{
	cout << "程序走了特化" << endl;
	return *s1 < *s2;
}


int main()
{
	test<int> a1(10);
	test<int> a2(20);
	//正常情况
	cout << "正常情况:" << myLess(10, 20) << endl;
	//特殊情况
	test<int>* pa1 = &a1;
	test<int>* pa2 = &a2;
	cout << "特殊情况:" << myLess(pa1, pa2);

	return 0;
}

如上,当我们实现一个函数的功能是比较大小时,如果此时我们实参传的是值的指针,那么函数就会读指针(即地址)进行比较,这样的结果是不确定的,因为申请的空间不确定谁大谁小,同时也不符合我们的预期。此时我们就需要对该指针类型进行特化,如上的test<int>*,根据特化步骤单独写一个特化模版函数,当传入的参数是test<int>*类型时,程序就会调用特化后的函数。

(通俗来讲就是,对某一个类型单独进行处理,当传入的实参类型与特化类型匹配的话就会进入特化后的模版函数)。

3、不建议使用函数模版特化

不建议使用函数模版特化,因为当遇见参数列表有引用或const修饰时的情况,处理起来会变得很麻烦。

上述情况用编译器优先匹配的原则也可以解决,有现成用现成。

bool myLess(test<int>* s1, test<int>* s2)
{
	cout << "编译器优先匹配" << endl;
	return s1 < s2;
}

(三)、类模版特化

1.全特化:

全特化即是将模板参数列表中所有的参数都确定化。
//类模板特化
//正常模版
template<class T1,class T2>
class myTest
{
private:
	T1 _a1;
	T2 _a2;
public:
	void fun()
	{
		cout << "调用正常模版" << endl;
	}
};

//全特化指针类型
template<>
class myTest<int*,int*>
{
private:
	int* _a1;
	int* _a2;
public:
	void fun()
	{
		cout << "调用全特化int指针模版" << endl;
	}
};


int main()
{
	//正常模版
	myTest<int,int> aa;
	aa.fun();
	//全特化int指针
	myTest<int*, int*> bb;
	bb.fun();
	return 0;
}

注意全特化模版的写法,template<>里面没有数据,将所有模版替换为具体类型(int*为例),这样当,类实例化时传递的参数与之匹配时,就会使用全特化模版。

2、偏特化

2.1、部分特化
将模板参数类表中的一部分参数特化
//偏特化
//正常模版
template<class T1,class T2>
class myTest
{
private:
	T1 _a1;
	T2 _a2;
public:
	void fun()
	{
		cout << "调用正常模版" << endl;
	}
};

//部分参数特化
template<class T1>
class myTest<T1,int*>
{
private:
	T1 _a1;
	int* _a2;
public:
	void fun()
	{
		cout << "调用部分参数模版" << endl;
	}
};
int main()
{
	myTest<char, int*> s1;
	s1.fun();
	return 0;
}

类似的,这里我们特化了第二个参数为具体的int*,那么当传模版参数中只要第二个模版参数的类型是int型,那么就会实例化部分参数特化的类。要注意写法。

2.2、参数更进一步的限制
偏特化并不仅仅是指特化部分参数,还可以是针对模板参数更进一步的 条件限制 所设计出来的一个特化版本。如限制类型是指针或者引用等。
//偏特化
//正常模版
template<class T1,class T2>
class myTest
{
private:
	T1 _a1;
	T2 _a2;
public:
	void fun()
	{
		cout << "调用正常模版" << endl;
	}
};

部分参数特化
//template<class T1>
//class myTest<T1,int*>
//{
//private:
//	T1 _a1;
//	int* _a2;
//public:
//	void fun()
//	{
//		cout << "调用部分参数模版" << endl;
//	}
//};

//进一步限制类型
template<class T1,class T2>
class myTest<T1*, T2*>
{
private:
	T1* _a1;
	T2* _a2;
public:
	void fun()
	{
		cout << "调用进一步限制成指针的模版" << endl;
	}
};

int main()
{
	myTest<char, int> s1;
	s1.fun();
	myTest<char*, int*> s2;
	s2.fun();
	myTest<double*, char*> s3;
	s3.fun();
	return 0;
}
上述写法将模版参数全部限制成指针,不管什么类型的指针,只要两个模版参数都是原生指针,那么就会实例化进一步限制的特化模版类。
2.3、注意:

(1)、全特化和进一步限制特化的区别,通俗来讲可以理解为,全特化只针对一种类型进行特化,而后者则是针对某一类类型,如指针类,引用类.

(2)、同时存在全特化和偏特化,则会优先匹配全特化,后匹配偏特化,因为全特化的类型更加明确,类似于编译器优先匹配原则。如果都匹配不上的就会匹配正常模版。

2.4、普通指针变量传递给const指针变量的引用(权限缩小)

我们知道,普通指针变量是可以传递给const修饰的指针变量的,这样所指向的值是不能改变的,这属于权限缩小问题。同时普通变量也可以传递给引用。

那么当把普通指针变量传递给const指针变量的引用该怎么书写呐?

错误写法:

因为当把int* 传递给const int* 的引用时,中间会产生一个临时变量而临时变量具有常属性,所以我们应该使用const指针变量的const引用进行接收:

正确写法:

三、模版的分离编译

1、什么是分离编译?

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链 接起来形成单一的可执行文件的过程称为分离编译模式。 例如将函数的声明放进 .h 文件,函数的定义放 .cpp 文件,程序翻译的前三个过程.h 和 .cpp是分别进行,最后链接阶段在链接起来。

2、模版的分离编译

函数模版和类模版都不支持声明和定义分离,所以最好的办法就是将声明和定义放在同一个源文件。

四、模版的优缺点

1.优点:

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

2、缺点:

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

标签:进阶,模版,函数,C++,参数,fun,特化,指针
From: https://blog.csdn.net/hffh123/article/details/143700591

相关文章

  • C++学习路线(求补充)
    研二女本硕211明年找工作看网上各种经验帖总结了个C++自学路线求各位大佬指正时间有点紧不知道学这些够不够学习内容:黑马C++基础语法书籍:C++primerplus1,2结束后开始刷代码随想录一天两道复习复习语法侯捷视频:侯捷-STL泛型编程(必看)侯捷-C++11新特性(必看)侯捷-......
  • 打卡信奥刷题(221)用C++信奥P1740[普及组/提高] Diamond A&B(1)
    DiamondA&B(1)题目背景由于本题较难,将本题拆做两题,分别为diamondA以及diamondB。本题为DiamondA。题目描述教主上电视了!这个消息绝对是一个爆炸性的新闻。一经传开,大街上瞬间就没人了(都回家看电视去了),商店打烊,工厂停业。大家都把电视机的音量开到最大,教主的声音......
  • 【C++学习笔记一】变量与运算符
    目录一、C++输出数据1.1数据的分类1.2输出数据二、注释2.1 单行注释2.2 多行注释2.3VS中的快捷键三、变量3.1声明变量3.2变量的赋值3.3示例四、常量4.1宏常量4.2const修饰的变量4.3示例五、标识符的命名5.1C++命名规则5.2C++关键字六、数据输......
  • 代码静态测试工具Klocwork 2024.3新版发布:Validate平台改进编码标准CC++
    Klocwork2024.3为C/C++分析引擎和构建上传流程引入了新功能和性能改进。此版本还附带了增强的安全性和用户体验改进,包括用于SAML/OIDC身份验证的IDE插件中更好的用户身份验证工作流程。其他增强功能包括更广泛的编码标准覆盖范围以及改进的与Bazel构建系统的集成。Vali......
  • C++ 中环境变量获取,动态库环境变量的传递
    C++中环境变量获取,动态库环境变量的传递在实践中,考虑最好还是使用getenv()函数来取得相关的环境变量.C/C++获取系统环境变量,其实是很简单的.函数名:getenv功能:从环境中取字符串,获取环境变量的值头文件:stdlib.h用法:char*getenv(char*envvar);getenv()用来取......
  • 南沙C++信奥赛老师解一本通题 1385:团伙(group)
    ​【题目描述】在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足:1、我朋友的朋友是我的朋友;2、我敌人的敌人是我的朋友;所有是朋友的人组成一个团伙。告诉你关于这n个人的m条信息,即某两个人是朋友,或者某两个人是敌人,请你编写一个程序,计算出这个城市最多可能有多......
  • C++求最小公倍数与最大公因数
    #大一小卡了咪的作业4题目:        设计两个函数MaxCommonDevisor(n,m) 和MinCommonMultiple(n,m),分别求两个数的最大公约数和最小公倍数。主函数调用上述两个函数,实现功能。    乍一看这个题其实比较麻烦,因为要同时满足两个数的要求(同时整除/分别整除),但实际......
  • C++-模块
    C++20引入了模块(Modules)的概念,这是一项重要的语言特性,旨在改进C++的编译速度和构建过程。模块提供了一种新的组织代码的方式,可以替代传统的头文件包含和预处理器。使用模块可以带来以下几个好处:更快的编译速度:传统的头文件包含方式在编译过程中需要进行大量的文件打开和文本处......
  • 【C++】模板(一):函数模板
    大家好,我是苏貝,本篇博客带大家了解C++的函数模板,如果你觉得我写的还不错的话,可以给我一个赞......
  • C++----继承
    目录一.继承的概念及定义1.继承的概念2.继承的定义二.基类和派生类对象赋值转化三.继承中的作用域四.派生类的默认成员函数五.继承和友元六.继承和静态成员七.复杂的菱形继承及菱形虚拟继承八.继承的总结和反思一.继承的概念及定义1.继承的概念继承(inheritanc......