【c++】深入学习c++中的模板

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

 文章来源地址https://www.toymoban.com/news/detail-406504.html

 

文章目录

  • 前言
  • 一、非类型模板参数
  • 二、模板的特化
    • 1.引入概念
    • 2.模板的分离编译
  • 总结

 


前言

我们在前几篇的学习中只是使用了最简单的模板,比如用一个模板类等等,我们这篇的学习将在以前的基础上再深入的学习一下模板。


 

一、非类型模板参数

模板参数分类类型形参与非类型形参
类型形参即:出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称
非类型形参,就是用一个常量作为类 ( 函数 ) 模板的一个参数,在类 ( 函数 ) 模板中可将该参数当成常量来使用。
下面我们来演示一下:
#define N 10
//类型模板参数
template<class T>
class array
{
public:

private:
	T _a[N];
};

int main()
{
	Array<int> a1;
	Array<double> a2;
	return 0;
}

在以前,我们想要在一个类中定义一个模板数组能实现存放不同个数的数组是不行的,因为我们define的N是一个固定值,如下图:

【c++】深入学习c++中的模板

 我们如何让两个数组存放的类型不同存放的个数也不同呢?这个时候非类型模板参数的效果就来了,如下:

//非类型模板参数
template<class T,size_t n = 10>
class Array
{
public:

private:
	T _a[n];
};

int main()
{
	Array<int,5> a1;
	Array<double,8> a2;
	return 0;
}

【c++】深入学习c++中的模板

 我们可以看到这两个数组类型不同,数组元素个数也不同。在这里要记住,非类型模板参数一定是整形常量,不可以是浮点数,类对象以及字符串。

【c++】深入学习c++中的模板

【c++】深入学习c++中的模板 讲到这里我们可以对比一下c++11的新特性的array.

【c++】深入学习c++中的模板

这个数组和传统数组的区别在于,传统数组对于越界检查是抽查,array对于越界检查是全面检查。

【c++】深入学习c++中的模板 【c++】深入学习c++中的模板

从上图我们可以看到,传统数组都越界写了竟然没有检查出来越界,而array是可以检查出来的。但是这个array我们会用吗?其实不会,因为我们直接用vector是最方便的,没必要用array,并且array的空间在栈上,如果数据过多会将栈搞崩溃,而vector空间在堆上完全不受影响。 

二、模板的特化

1.引入概念

class Date
{
public:
	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);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};
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, 8);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl;  //可以比较,结果错误
	return 0;
}

对于上面的代码我们用了以前的日期类做例子,然后用一个Less函数比较日期的大小,通过结果发现对于指针的比较是错误的,如下图:

【c++】深入学习c++中的模板

可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指针的地址,这就无法达到预期而错误。

 那么这个时候该怎么办呢?我们就需要对模板进行特化,只针对Date*类型,如下图:

//模板特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}

 对于模板特化我们必须在函数名后面显式类型,看一下特化后的结果:

【c++】深入学习c++中的模板

 可见确实完成了我们的任务,刚刚我们演示的模板特化被称为全特化,就是参数的类型都是确定的。

//模板特化   半特化
template<class T>
bool Less(T* left, T* right)
{
	return *left < *right;
}

【c++】深入学习c++中的模板 我们的特化不一定非要确认类型,比如上面的半特化直接将所有指针类型包含了,我们的两个int*的指针去比较结果也是正确的。

模板特化分为函数模板特化和类模板特化。

下面我们讲解一下类模板特化:

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; }
private:
	int _d1;
	char _d2;
};

int main()
{
	Date<int,int> d1;
	Date<int, char> d2;
	return 0;
}

对于类模板特化我们可以特化不同版本的类,比如上面代码中的(int,int)的类和(int,char)的类。并且上述代码中都是全特化,将模板参数列表中所有的参数都确定化,下面我们在演示一下偏特化:

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

偏特化就是对参数的进一步限制。我们演示一下偏特化的部分特化:

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
 Data() {cout<<"Data<T1, int>" <<endl;}
private:
 T1 _d1;
 int _d2;
};

 当然我们的模板参数也可以是引用:

//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
 Data(const T1& d1, const T2& d2)
 : _d1(d1)
 , _d2(d2)
 {
 cout<<"Data<T1&, T2&>" <<endl;
 }
 
private:
 const T1 & _d1;
 const T2 & _d2; 
 };

 引用做模板参数不是很常见,但是我们在实现list的迭代器的时候见到过模板参数是引用。

2.模板的分离编译

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

模板是不支持分离编译(在两个文件)的,会引发奇怪的报错。 如下图:

【c++】深入学习c++中的模板

【c++】深入学习c++中的模板 【c++】深入学习c++中的模板

 再说为什么出现这种情况之前我们先讲一下c/c++程序运行一般要经过的几个步骤:

【c++】深入学习c++中的模板

 我们发现同样的方式我们定义一个函数就能正常运行,如下图:

【c++】深入学习c++中的模板

【c++】深入学习c++中的模板

【c++】深入学习c++中的模板【c++】深入学习c++中的模板

 现在我们就通过汇编分析一下为什么会这样:

【c++】深入学习c++中的模板

 我们发现函数func会跳到0611348h这个函数地址,在这里func会被编译成一堆指令,所以在func.o中有函数的地址,而模板是没有函数的地址的,因为模板需要实例化后才有具体的地址,刚开始地址为空如下图:

【c++】深入学习c++中的模板

在编译的时候在地址处打个问号,等到链接的时候再去拿Add的地址,如下图:

【c++】深入学习c++中的模板

等到链接的时候找到func的地址,但是却找不到add的地址,如下图:

【c++】深入学习c++中的模板 也就是说模板分离编译出错的原因是链接的时候无法拿到模板函数的地址,那么有办法可以解决吗?其实是有的,但是不常用因为每个类型都需要显示实例化:

【c++】深入学习c++中的模板

【c++】深入学习c++中的模板

 这个时候我们把int类型的add放出来又不行了:

【c++】深入学习c++中的模板

 要想解决还是刚刚显示实例化的方法,但是这样就会很繁琐。所以我们最好的办法就是不要分离在两个文件,就在一个文件中实现。如下图:

【c++】深入学习c++中的模板

【c++】深入学习c++中的模板以上就是本篇文章的所有内容,最重要的是理解为什么模板分离编译会报错并且如何解决这个问题

注意:

对于模板的报错,我们一定要先解决报错的第一个,因为通常模板是有一个问题带出来的其他好多问题,将第一个问题解决了就解决了其他问题。


总结

【优点】
1. 模板复用了代码,节省资源,更快的迭代开发, C++ 的标准模板库 (STL) 因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

 

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

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

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

相关文章

  • 【C++】C++学习前言

    C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(objectoriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。

    2024年03月12日
    浏览(41)
  • WordPress使用自定义文章类型实现任意模板的方法

    本文实例讲述了WordPress使用自定义文章类型实现任意模板的方法。分享给大家供大家参考,具体如下: 这几天在搭建博客的时候,碰到了一个对我来说很棘手的问题。WordPress自带的文章类型只能使用他们特定的模版,而我由于想做一个心情时间轴的板块,所以只能自定义文章

    2023年04月24日
    浏览(35)
  • 【自制C++深度学习框架】前言

    此GitHub项目是一个初学者的深度学习框架,使用C++编写,旨在为用户提供一种简单、易于理解的深度学习实现方式。以下是本项目的主要特点和功能: 计算图:使用计算图来描述深度学习模型的计算过程,利用计算图将神经网络的计算过程视为一个有向无环图。通过构建计算

    2024年02月07日
    浏览(32)
  • 【C++初阶(一)】学习前言以及命名空间

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++初阶之路⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习排序知识   🔝🔝 对于复杂的问题,规模较大的程序 需要高度的抽象和建模时 C语言不再适合应用于这种场景 于是在1982年 C++创始人 Bjarne Stroustrup 在C语言

    2024年02月11日
    浏览(40)
  • 【C++初阶】前言——C++的发展简述及学习方法分享

     ========================================================================= 主页点击直达: 个人主页 我的小仓库: 代码仓库 C语言偷着笑: C语言专栏 数据结构挨打小记: 初阶数据结构专栏 Linux被操作记: Linux专栏 LeetCode刷题掉发记: LeetCode刷题 算法: 算法专栏  C++头疼记: C++专栏 ====

    2024年02月08日
    浏览(50)
  • 【C++初阶(一)】学习前言 命名空间与IO流

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 C++是基于C语言而产生的,它既可以进行C语言的

    2024年02月08日
    浏览(85)
  • 【C++】再谈模板,深入理解C++模板

    🎉博客主页:小智_x0___0x_ 🎉欢迎关注:👍点赞🙌收藏✍️留言 🎉系列专栏:C++初阶 🎉代码仓库:小智的代码仓库 在C++中, typename 和 class 都可以用于模板参数声明,它们的作用是相同的,用于指定一个类型参数。但是,在一些情况下, typename 比 class 更加灵活。

    2024年02月16日
    浏览(18)
  • Linux 学习目录合集【文章索引】

    前言:本内容为笔者自学笔记内容。 本文中的操作环境:腾讯云服务器:CentOS 7.6 64bit 学习阶段规划: Linux 基本操作【基本命令、vim、makefile使用等】 Linux 系统【进程:概念、控制、通信;IO基础;多线程等】 Linux 网络【网络基础、套接字编程、IO高级等】 学习集: C++ 入门

    2024年02月07日
    浏览(38)
  • 【C++】模板初阶 【 深入浅出理解 模板 】

    如何实现一个通用的交换函数呢? 使用函数重载虽然可以实现 ,但是有一下几个不好的地方: 重载的函数 仅仅是类型不同 ,代码复用率比较低, 只要有新类型出现时,就需要用户自己增加对应的函数 代码的可维护性比较低,一个出错可能所有的重载均出错 那能否 告诉编

    2024年02月05日
    浏览(36)
  • 【C++】模板进阶——非类型模板参数、模板特化、模板分离编译

    模板参数分为类型形参 与 非类型形参 类型形参 :出现在模板参数列表中,跟在class或者typedename之类的参数类型名称。 非类型形参 :用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。 非类型模板参数的优势: 有些容器需要在创建对象

    2024年02月01日
    浏览(34)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包