STL学习小记--与C++模板相关的几个特性
先谈谈模板在我脑子里的典型吧
template<class T> const T& GetMax(const T& t1, const T& t2) {return t1>t2?t1:t2; }
如上面的代码,这是一个模板函数(template function)。要使用这个函数,传入的参数必须满足>运算符的条件。在C#中有where关键字,很可惜C++的模板没有这种限制。
而据说BS本人也一再声明C++的模板不需要这种限制,作为一个初学者,其中缘由就不得而知了。
template并非一次编译便生成适合所有类型的代码,而是针对被使用的某个(组)类型进行编译。这导致一个很严肃的问题:实际处理template的时候,面对template function,必须先提供一个实际的例子,然后才能调用,这样才能通过编译。所以使用template function的话,需要将整个函数定义在头文件中,在别处使用也需要提供源码。C++标准中似乎有对于使template运行可移植性的方法(编译成dll),但是貌似我使用的编译器都不支持。
模板和宏在功能上相似,这类的问题经常引起大神们的口水战,吾辈且避之。
下面登记下模板中使用的几个特性:
1, Nontyoe Template Parameters(非类型模板参数)
第一次遇到这个是在学习STL的bitset的时候。后来发现很多书在引入这内容的时候都是拿bitset举例。
bitset<32> bs32;
作为初学者的我,刚见到的时候非常疑惑,原来模板还有这种用法,可是这种方式来初始化构造的话,为什么不直接用构造函数呢?就像是建一个32长度的数组一样的方式啊。
可是,32长度的数组不是一个类型,同样,用构造函数创建的32长度容器也不是某一类型,而是某一类型的实例。而bitset<32>则是一个类型,档次果然就不一样了呀。
即是说,如果继续声明一个bitset<32> bs32X;那么bs32X和bs32的类型是一样的,用RTTI来鉴别,typeid就是一样的。这样bs32和bs32X是允许相互赋值的。
而如果声明一个bitset<33> bs33; 那就不是同一类型了。
嗯,模板类是在类级别,而一个是在实例级别,档次呀档次。。。
2,Default Template Parameters(缺省模板参数)
C++允许函数有缺省参数, 很方便地, 也允许模板参数有缺省值
比如下例:
template <class T, class NT= int> class P { public:T m_t;NT m_nt; };
这样在使用的时候,即使只是定义
void f() {P<int> p; //... }
这样也会被默认为P<int, int>的类型。很方便地特性,和函数缺省参数一样,能减少不少代码量。
3, typename
定义一个模板类或是模板函数的时候,需要制定类型的名字,使用T似乎已经是一种潜规则了。
而T前面可以使用typename,也可以使用class来表示后面的标识符是一个类型名。
而关键字typename还可以被用来作为类型前的标识符号。
比如:
template <class T> class P {typename T::MyType* pT; };
如果不使用typename关键字,按照C++的一般规则,除了typename修饰之外的任何template中的标识符都被视为一个值而非一个类型,所以编译器就会认为MyType是T中的一个成员,这一行被解释为MyType和pT相乘。调用语句写在类定义中编译器就要报错了。
使用上面的类模板,传入的模板参数必须满足类型中定义了MyType类型,可以使用typedef,可以直接定义class。
4, Member Template(成员模板函数)
全局函数可以是一个template, 同样C++也允许类成员函数是一个template。但是这样的成员函数不能使虚函数,而且也不能有缺省参数。(话外音:那不就是一个带this指针参数的全局函数而已吗?)
比如:
class A { public:template<class T>void f(T t){//... } };
然后使用的时候只需要实例化一个A类型的变量就能对各种适用的类型使用f函数了。f函数的具体实现不影响A类型,相当方便。
另外需要提到的是,这个特性常常会被用来作为模板类之间类型转换。
如同在第一个特性中提到的,根据模板参数的不同,类型也会不同,比如MyClass<int>和MyClass<double>属于两个不同的类型,那么两者之间是不能直接转换的,即使int可以转换为double。为了满足这种懒惰成性的需求,就可以使用这个特性。
如下代码:
template <class T> class A { public:T m_value;A():m_value(){}A(const T& t){m_value = t;}template<class T2>void Parse(const A<T2>& t){this->m_value = t.m_value;} };int main() {A<int> a1(102);A<double> a2(12.222);A<char> a3('a');a1.Parse(a2);cout<<a1.m_value<<endl;//输出12a2.Parse(a3);//字符'a'将转化为ASCII码cout<<a2.m_value<<endl;//输出97system("pause"); }
其实这里既然是int转double,那么也可以做成直接用a1来初始化a2。也就是在a2的构造函数中使用a1作为参数。(话外: 构造函数也是成员函数,满足非虚函数亦没有缺省参数的条件)
template <class T> class A { public:T m_value;A():m_value(){}A(const T& t){m_value = t;}template<class T2>A(const A<T2>& t){m_value = t.m_value;} };int main() {A<int> a1(97);A<int> a2(a1);A<char> a3(a1);cout<<a1.m_value<<endl;//97cout<<a2.m_value<<endl;//97cout<<a3.m_value<<endl;//asystem("pause"); }
这里a2构造的时候调用的是默认的拷贝构造函数,而a3构造的时候使用的是模板拷贝构造函数。
5, Nested Template(嵌套模板类)
类中定义的函数可以是模板函数,类中定义的嵌套类也可以是个模板类。
template <class T> class MyClass {//... public:template <class T2>class NestedClass{//... }; };
另外的注意点
面试题是个很有意思的东西,往往能遇到许多稀奇古怪的状况。以前曾在面试中遇到这样的问题:
int* pI = new int;
int* pI2 = new int();
上面两行代码有什么区别。
我大学汇编是玩NDS上课的,所以无法从那个方向去分析。只是根据实际使用来猜想, 前者分配了空间却没有初始化值,后者分配了空间并初始化值。
这和下面的情况相似(貌似使用自定义类型的时候不一定了):
int i; //undefined value
int i2 = int(); //initialized with zero
这是基本类型int,那么自定义类型呢?
自定义的class有时候不会显式地定义一个构造函数。
struct XX {//XX():n(){}int n; };int main() {XX* pX1 = new XX;XX* pX2 = new XX();cout<<pX1->n<<", "<<pX2->n<<endl; }
输出的结果是一个未初始化的值和0
那如果自定义的类型已经有一个无参构造函数了呢?那就会调用这个无参构造函数来初始化了。
所以,以后一定要养成定义一个无参构造函数并初始化成员的好习惯啊%>_<%
那么,使用模板的时候呢,使用这个语法可以保证该类型实例能被初始化为一个确切的初值。
摘自:ISO/IEC 14882:2003(E) 5.3.4 - 15
— If the new-initializer is omitted:
— If T is a (possibly cv-qualified) non-POD class type (or array thereof), the object is default-initialized(8.5). If T is a const-qualified type, the underlying class type shall have a user-declared default constructor.
— Otherwise, the object created has indeterminate value. If T is a const-qualified type, or a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of const-qualified type, the program is ill-formed;
— If the new-initializer is of the form (), the item is value-initialized (8.5);
转载于:https://www.cnblogs.com/pasoraku/archive/2013/04/12/3016782.html
STL学习小记--与C++模板相关的几个特性相关推荐
- C++ STL学习笔记 : 1. template 模板函数
本篇文章是学习C++ STL库的第一篇笔记,主要记录了使用template关键字创建模板函数的方法. 下面用一个非常简单的例子解释模板函数的用法 : #include <iostream> ...
- (2013.01.18-2013.07.15)179天的学习小记
(2013.01.18-2013.07.15)179天的学习小记 好久没有做个小小结咯,我的第一天学习小记是从2011.07.04开始,那时说好了在大学期间要每天记录,自我监督,就这样,这事也干了两年 ...
- ICRA 2022 | CaTGrasp: 从模拟器中学习类别级的任务相关的抓取姿态
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨泡泡机器人 来源丨泡泡机器人SLAM 标题:CaTGrasp: Learning Categor ...
- 多项式快速插值学习小记
今天终于抽空把这个综(du)合(liu)知识点学了,心力交瘁-- 多项式快速插值 给出 nnn 个点 (xi,yi)(x_i,y_i)(xi,yi) ,要求一个次数为 n−1n-1n−1 的多项式 ...
- 多项式的ln、exp、快速幂和开根学习小记
不妨又学习了一下多项式的求ln.exp.快速幂和开根操作. 这些操作比之前的求逆更上了一层台阶,应用同样很广. 多项式求逆等知识在我的博客里有讲:多项式的求逆.取模和多点求值学习小记 多项式ln 给出 ...
- python 温度 符号_【火马】Python学习小记01
Python 学习小记 Life is short,you need Python! 写在前面 自从重新拾起2016年开始注册的公众号"火马编程",我就把TA当作了自己的一块&qu ...
- C++ STL学习笔记
C++ STL学习笔记一 为何要学习STL: 数据结构与算法是编程的核心,STL中包含各种数据结构和优秀的算法,确实值得深入学习,本文中虽然着重使用,但希望有心的朋友能多看看相关数据结构的实现,对于C ...
- C++STL学习笔记(4) 分配器(Allocator)
在前面的博客<C++ STL学习笔记(3) 分配器Allocator,OOP, GP简单介绍>中,简单的介绍了分配器再STL的容器中所担当的角色,这一节对STL六大部件之一的分配器进行详细 ...
- C++ STL学习笔记(3) 分配器Allocator,OOP, GP简单介绍
继续学习侯捷老师的课程! 在前面的博客<C++ STL学习笔记(2) 容器结构与分类>中介绍了STL中常用到的容器以及他们的使用方法,在我们使用容器的时候,背后需要一个东西支持对内存的使用 ...
最新文章
- 你想要的宏基因组-微生物组知识全在这(1802)
- 【RAC】RAC 实现IP访问控制
- emacs命令速查 摘
- tuple parameter unpacking is not supported in python3
- python语言结构_Python语言表示语句结构时采用
- [How TO]-Ubuntu 20.04修改终端标题栏文字
- mysql范式与反范式_给女同事讲解MySQL数据库设计范式与反范式,她夸我“技术好”...
- ie6 css padding,IE6padding的一个影响布局的问题在
- Linux系统编程21:基础IO之全缓冲和行缓冲的区别及深刻理解缓冲区及其作用
- nginx正向代理https,zabbix内网实现企业微信告警
- python中if elif else流程图_Python中的if、else、elif语句用法简明讲解
- C# Random生成相同随机数的解决方案
- 这台计算机似乎没有安装操作系统_【每日一练】计算机一级考试(一)
- Android系统自带样式(@android:style/) (转)
- AST介绍:解析html生成语法树
- 【mysql】复制一张表的数据到另一张表
- begin tran创建事务、commit tran提交事务、rollback tran回滚(撤消)事务的用法及理解
- Python + qrcode 实现文字转二维码
- 2021Q2产品团队绩效总结--管理日记
- python自学多久能精通-python多久可以精通