admin管理员组文章数量:1794759
C++之模板<template>
目录
- 🌈前言
- 🌸 模板
- 🌷1、泛型编程
- 🌺2、函数模板
- 🍀2.1、函数模板的概念
- 🍁2.2、函数模板的格式
- 🍂 2.3、函数模板的原理
- 🍌2.4、函数模板的实例化
- 🍃 2.5、模板参数的匹配原则
- 🍒 3、类模板
- 🍕 3.1、类模板的概念
- 🍔 3.2、类模板的格式
- 🍒3.3、类模板的实例化
- 🍖 4、非类型模板参数
- 🍗 5、模板的特化
- 🍙5.1、特化的概念
- 🍛5.2、函数模板的特化
- 🍜5.3、类模板特化
- 🍝 5.3.1、类模板全特化
- 🍞 5.3.2、类模板的偏特化
- 🍟6、模板的分离编译
- 🎆6.1、 什么是分离编译
- 🎆6. 2、解决方法
- 🎇7、模板内嵌类型无法识别问题
- 🎇8、模板总结
通过这篇文章,我们可以了解到模板的机制和基础语法!!!
🌸 模板模板是构建一个通用函数或类的蓝图或者说是公式,是基于蓝图来创建的!!!
🌷1、泛型编程如何实现一个通用的编程呢?
//在没有泛型编程时,只能使用函数重载逐个写 void Sweap(int& pa, int& pb) { int temp = pa; pa = pb; pb = temp; } void Sweap(double& pa, double& pb) { double temp = pa; pa = pb; pb = temp; } void Sweap(char& pa, char& pb) { char temp = pa; pa = pb; pb = temp; } //...............................使用函数重载虽然可以实现,但是有一下几个不好的地方:
那能不能生成一个模子,让编译器根据不同的类型利用该模子来生成代码呢?
- 如果在C++中,也能够存在这样的一个模具
- 通过给这个模具中填充不同材料(类型),来获得不同材料料的铸件(即生成具体类型的代码),那将会节省许多头发巧的是前人早已将树栽好,我们只需在此乘凉
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础 模板分为:函数模板和类模板
🌺2、函数模板 🍀2.1、函数模板的概念- 函数模板代表了一个函数的家族,该函数模板与类型无关
- 在使用时被参数化,根据实参类型来推导模板类型参数的特定版本
注意:函数模板只是一个蓝图,在实例化时,根据推导的参数的类型来生成特定的版本
🍁2.2、函数模板的格式
template<typename T1, typename T2…, typename Tn> 函数返回类型 函数名 (参数列表) { }
template <typename T> void Sweap(T& pa, T& pb) { T temp = pa; pa = pb; pb = temp; }程序解析:
- 模板定义从template关键字开始,后面的尖括号<>为模板参数列表
- 尖括号<>中的typename关键字是模板的类型参数,可以将其看做类型说明符
- 类型参数可以用来指定函数返回类型或函数参数类型…
注意:typename是用来定义模板参数的关键字,也可以使用class(切记:不能使用struct代替class)
//C++早期就是使用class来定义模板参数的 template <class T> void Sweap(T& pa, T& pb) { T temp = pa; pa = pb; pb = temp; } //不可以,可能struct只是为了兼容C而存在的,模板中不支持它 template <struct T> void Sweap(T& pa, T& pb) { T temp = pa; pa = pb; pb = temp; }🍂 2.3、函数模板的原理
原理:
- 函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具
- 所以其实模板就是将本来应该我们做的重复的事情交给了编译器
-
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用
-
比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此
注意:反汇编中的call Sweap< double >是指定了特定的类型来进行函数模板的实例化
🍌2.4、函数模板的实例化
概念:
- 用不同的类型的参数使用函数模板时,称为函数模板的实例化
- 模板实例化分为:隐式实例化和显式实例化
注意:当形参与实参类型不符合时,引用会指向一个生成的临时变量,必须加const
注意:如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错
🍃 2.5、模板参数的匹配原则
注意:非模板函数的匹配优先级比函数模板高,因为函数不需要实例化
🍒 3、类模板 🍕 3.1、类模板的概念
- 类模板(class template)是用来生成类的蓝图(模子)的
- 与函数模板不同的是:不能隐式为类模板推断模板类型参数的类型
- 实例化时需要显式指定模板参数类型
🍔 3.2、类模板的格式
template<typename T1, typename T2…, typename Tn> class 类名{ };
//简单的类模板定义 template <typename T> class A { public: A(T a) : _a(a) {} private: T _a; };Ps:定义与函数模板无恙,都是使用template关键字,后面跟上<>模板参数列表,<>里面为类型参数(类型说明符)typename/class
//动态顺序表,简单实现重要接口 template <typename T> class Vector { public: Vector(size_t capacity = 10) : _pData(new T[capacity]) , _size(0) , _capacity(capacity) {} //在类外演示类模板成员函数的定义 ~Vector(); void PushBack(const T& data) { if (_size == _capacity) { T* p = new T[_capacity * 2]; for (int i = 0; i < _size; ++i) { p[i] = _pData[i]; } _pData = p; _capacity *= 2; } _pData[_size] = data; ++_size; } // ...... private: T* _pData; size_t _size; size_t _capacity; }; // 类模板成员函数在类外定义:必须加上类模板的参数列表 template <typename T> Vector<T>::~Vector() // 必须指定类域,~Vector是属于Vector<T>类模板的成员函数 { if(_pData) delete[] _pData; _size = 0; _capacity = 0; } int main() { // 模板实例化,等下会讲到 Vector<int> v1(1); v1.PushBack(1); v1.PushBack(2); v1.PushBack(3); v1.PushBack(4); return 0; }注意:在类外定义类模板成员函数时,必须带上模板参数列表!!!
🍒3.3、类模板的实例化
- 类模板实例化与函数模板实例化不同
- 类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化后的类才是真正的类
注意:类模板实例化也叫:显式实例化,将特定的类型绑定到模板参数列表中…
🍖 4、非类型模板参数
概念:
- 模板类型参数分为二种:类型参数和非类型参数(常量值)
- 类型参数:出现在模板参数列表中,<>中存在class或typename的参数类型名称
- 非类型参数:使用一个常量作为类(函数)模板的某个参数,在类(函数)模板中可将该参数当初常量来使用
举个栗子🌰:
namespace Array { // 模板参数列表中指定了类型,还可以给定"缺省值" 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() { // 非类型模板参数,必须使用常量/表达式(右值)值进行传参 Array::array<int, 5> a; for (int i = 0; i < 5; ++i) a[i] = i; return 0; }注意:
🍗 5、模板的特化 🍙5.1、特化的概念
- 通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:
结论:
🍛5.2、函数模板的特化
函数模板的特化步骤:
注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出
🍜5.3、类模板特化 🍝 5.3.1、类模板全特化
全特化即是将模板参数列表中所有的参数都确定化:
namespace N { // 类模板参数表(<>)中的类型参数(typename/class)和非参数类型(int n)都可以定义默认值(类型) template <typename T1, typename T2 = int, int n = 100> class M { private: T1 a; T2 b; public: M() : a(0), b(0) {} M(const T1 &a_, const T2 &b_) : a(a_), b(b_) {} void Show() const { cout << a << ' ' << b << ' ' << n << endl; } }; } // 全特化(特定的)类模板成员函数-> template后面尖括号为空时,说明是全特化 template <> void N::M<double, char>::Show() const { cout << a << ' ' << b << endl; } // 全特化类模板-> template后面尖括号为空时,说明是全特化 template <> class N::M<char *, int> { private: char *str; int size; public: M() : size(0) { str = new char[0]; str[0] = '\\0'; } M(const char *st, int sz) : size(sz) { str = new char[sz]; strcpy(str, st); } ~M() { delete[] str; } void Show() const { cout << str << endl; } };🍞 5.3.2、类模板的偏特化
偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类:
template<class T1, class T2> class Data { public: Data() { cout << "Data<T1, T2>" << endl; } private: T1 _d1; T2 _d2; };偏特化有以下两种表现方式:
- 部分特化:将模板参数类表中的一部分参数特化
- 参数更进一步的限制:偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
🍟6、模板的分离编译 🎆6.1、 什么是分离编译
什么是分离编译?
- 一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式
假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:
// 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; }注意:每个.cpp文件都会单独编译生成.obj(目标文件),main.obj找不到call Add<int.>(1, 2) 和 call Add<double.>(1.0, 2.0)的实例化,然后将希望寄托于链接器,但是test.obj中的定义的模板没有被实例化(没有被调用),所以链接时会导致错误!!!
🎆6. 2、解决方法注意:在无实参调用下实例化时,template后面没有<>,后面跟指定类型的函数
【分离编译扩展阅读】
🎇7、模板内嵌类型无法识别问题
前言:在迭代器/类型traits技术会经常用到取模板中内嵌类型的代码,如果要在其他模板中访问模板中的内嵌类型时,因为模板还没实例化,不清楚这个内嵌类型是什么东西
栗子:
template <typename T> class Test { public: typedef Test<T> t; }; template <typename T> Test<T>::t func() // Error { static Test<T> t; return t; }问题解析:
解决方案:
🎇8、模板总结
【优点】
【缺陷】
全部知识点已经写完,感谢支持!!!
版权声明:本文标题:C++之模板<template> 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1686786084a102702.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论