【C++】进阶模板

这篇具有很好参考价值的文章主要介绍了【C++】进阶模板。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我们在 初识模板 中已经初步接触过模板了,下面我们开始更进一步学习模板。

一、非类型模板参数

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

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

例如我们定义一个 Stack 类,我们在实例化的时候传入需要用的空间大小,这样就可以避免扩容或者空间浪费的问题了;如下:

			template <class T, size_t N>
			class Stack
			{
			public:
				Stack()	
				{}
			
			private:
				T _a[N];
			};
			
			int main()
			{
				Stack<int, 100> st1;
				Stack<double, 1000> st2;
			
				return 0;
			}

注意:

  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的,只支持整型。
  2. 非类型的模板参数必须在编译期就能确认结果。

二、模板的特化

1. 函数模板的特化

函数模板的特化步骤:

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

例如,我们要对日期类的指针进行比较,如果直接走模板生成的函数,就会按照指针的大小进行比较,但这并不是我们想要的,所以我们可以对函数模板进行特化,如下:

		// 函数模板特化
		// 函数模板 -- 参数匹配
		template<class T>
		bool Less(T left, T right)
		{
			return left < right;
		}
		
		// 对Less函数模板进行特化
		template<>
		bool Less<Date*>(Date* left, Date* right)
		{
			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 = &d1;
			Date* p2 = &d2;
		
			cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
		
			return 0;
		}

2. 类模板特化

假设有一个日期类,它的原模板如下:

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

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

		// 全特化
		template<>
		class Date<int, char>
		{
		public:
		    Date() { cout << "Date<int, char>" << endl; }
		};

我们实例化两个对象,分别调用原模板和全特化模板:

【C++】进阶模板,C++,c++,chrome,开发语言

  1. 偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于上面的日期类原模板。

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

  • (1)部分特化:将模板参数类表中的一部分参数特化。

例如:

			// 偏特化1.
			// 将第二个参数特化为int
			template <class T1>
			class Date<T1, int>
			{
			public:
			    Date() { cout << "Date<T1, int>" << endl; }
			};
  • (2)参数更进一步的限制

偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。

例如:

		// 偏特化2.
		// 两个参数偏特化为指针类型
		template <class T1, class T2>
		class Date <T1*, T2*>
		{
		public:
		    Date() { cout << "Date<T1*, T2*>" << endl; }
		};

我们分别针对两种偏特化的模板实例化对象,如下:

【C++】进阶模板,C++,c++,chrome,开发语言

我们可以看到,编译器确实调用了特化的 int 版本和特化的指针版本

如果有多个模板符合实例化的对象,编译器会选择最优的那一个进行实例化。

3. 模板特化的应用

假如有如下专门用来按照小于比较的类模板 Less

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

我们实例化出几个对象,并将它们放入 vector<Date> 中进行排序:

		int main()
		{
		    Date d1(2023, 10, 7);
		    Date d2(2023, 10, 6);
		    Date d3(2023, 10, 8);
		
		    vector<Date> v1;
		    v1.push_back(d1);
		    v1.push_back(d2);
		    v1.push_back(d3);
		
		    // 可以直接排序,结果是日期升序
		    sort(v1.begin(), v1.end(), Less<Date>());
		
		    return 0;
		}

我们以上的方法是可以直接排序的,但是现在我们要将 d1、d2、d3 的地址放入 vector<Date*> 中呢?例如:

		int main()
		{
		    Date d1(2023, 10, 7);
		    Date d2(2023, 10, 6);
		    Date d3(2023, 10, 8);
		
		    vector<Date*> v2;
		    v2.push_back(&d1);
		    v2.push_back(&d2);
		    v2.push_back(&d3);
		
		    // 不能直接排序,结果是错误的
		    sort(v2.begin(), v2.end(), Less<Date*>());
		
		    return 0;
		}

如果我们还是以上面的小于比较的类模板进行排序,结果是错误的,因为按照上面的比较方法比较的是地址,而地址的大小每次传入是不一样的,所以每一次比较的大小都是不一样的;所以我们可以对 Less 类模板按照指针的方式特化,如下:

		// 对Less类模板按照指针方式特化
		template<>
		struct Less<Date*>
		{
		    bool operator()(Date* x, Date* y) const
		    {
		        return *x < *y;
		    }
		};

特化之后,在运行上述代码,就可以得到正确的结果。

三、模板的分离编译

1. 分离编译

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

2. 模板的分离编译

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

		// a.h
		template<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;
		}

C/C++ 程序要正常运行,一般要经过以下步骤:预处理 --> 编译 --> 汇编 --> 链接

a.cpp 中,编译器没有看到对 Add 模板函数的实例化,因此不会生成具体的加法函数。

在链接阶段,编译器会将多个obj文件合并成一个,并处理没有解决的地址问题;而在 main.obj 中调用的 Add< int >Add< double > ,编译器在链接时才会找其地址,但是这两个函数没有实例化没有生成具体代码,因此链接时报错。

3. 解决方法

  1. 声明和定义放到一个文件 “xxx.hpp” 里面或者 “xxx.h” 其实也是可以的,推荐使用这种。
  2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。

四、模板总结

【优点】

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

【缺陷】文章来源地址https://www.toymoban.com/news/detail-718727.html

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

到了这里,关于【C++】进阶模板的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】模板进阶|继承

    模板参数 分为 类型形参 和 非类型形参 类型形参 即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参 ,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。 这里我们引入一个例子来说明问题: 这里有

    2023年04月08日
    浏览(40)
  • C++之模板进阶

    模板参数分两种: 类型形参 与 非类型形参 。 类型形参 :出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参 :就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。 注意 : 浮点数、类对象以及字符串是不

    2024年02月12日
    浏览(26)
  • C++模板进阶

    模板参数分为类型模板参数和非类型模板参数; 类型模板参数:就是在模板参数列表中出现在class或typename之后的参数; 非类型模板参数:出现在模板参数列表的整数,该整数是常量,不可更改,可以作为模板的参数来使用; 类模板: 函数模板: 对于类型模板参数来说

    2023年04月08日
    浏览(25)
  • 【C++】进阶模板

    我们在 初识模板 中已经初步接触过模板了,下面我们开始更进一步学习模板。 模板参数分 类类型形参 与 非类型形参 。 类型形参 :出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称。 非类型形参 :就是用一个常量作为类(函数)模板的一个参数,在类(函数

    2024年02月08日
    浏览(29)
  • C++【模板进阶】

    ✨个人主页: 北 海 🎉所属专栏: C++修行之路 🎃操作环境: Visual Studio 2019 版本 16.11.17 模板是搭建 STL 的基本工具,同时也是泛型编程思想的代表,模板用好了可以提高程序的灵活性,以便进行更高效的迭代开发,模板除了最基本的类型替换功能外,还有更多高阶操作:非

    2024年02月04日
    浏览(24)
  • C++:模板进阶

      目录 1.非类型模板参数 2. 模板的特化 2.1 概念 2.2 函数模板的特化 2.3 类模板的特化 2.3.1 全特化 2.3.2 偏特化 3. 模板的分离编译 3.1 什么是分离编译 3.2 模板的分离编译 3.3 解决方法 4. 模板总结 模板参数分类: 类型形参 与 非类型形参 。 类型形参即:出现在模板参数列表中

    2024年02月05日
    浏览(22)
  • C++ 模板进阶

    目录 一、非类型模板参数 二、模板的特化 1、函数模板特化 2、类模板特化 全特化 偏特化 3、例题 三、模板分离编译 1、定义 2、解决方法 3、模板总结 模板参数分类类型形参与非类型形参。 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非

    2024年02月02日
    浏览(23)
  • C++——模板进阶

    🌸作者简介: 花想云 ,在读本科生一枚,致力于 C/C++、Linux 学习。 🌸 本文收录于 C++系列 ,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,专为大学生打造全套 C++ 学习教程,持续更新! 🌸 相关专栏推荐: C语言初阶系列 、 C语言进阶系列 、 数据结构与算法 、 Linu

    2024年02月01日
    浏览(28)
  • C++:模板进阶与继承

    1.非类型的模板参数 模板参数:类型形参和非类型形参。 类型形参 :出现在模板参数列表中, 跟在class或者typename之后 的参数类型名称。 非类型形参 :就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可 将该参数当成常量来使用 。 注意: 非类型参数其实很

    2024年02月08日
    浏览(40)
  • C++:模板进阶

    在C++:模板初阶中,已经介绍过了模板的基本用法,接下来将着重讲解模板特化等问题。 模板参数分为:类类型形参与非类型形参。 类类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参,就是用一个常量作为类(函数)模板的一个参数,

    2024年02月03日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包