《Effective C++》读书笔记VI

模板与泛型编程(Templates and Generic Programming

模板元编程(Template metaprogramming):在C++编译器内执行并于编译完成时停止执行的程序。

条款41:了解隐式接口和编译器多态(Understand implicit Interface and compile-time polymorphism.

面向对象编程世界总是以显示接口(explicit interface)和运行期多态(runtime polymorphism)解决问题。

1Templates及泛型编程的世界,与面向对象有根本上的不同。在此世界中显示接口和运行期仍然存在,但重要性降低。反倒是隐式接口(implicit interface)和编译期多态(compile-time polymorphism)移到前头。

 

 

 

 

 

“运行期多态”和“编译期多态”之间的差异,类似于“哪一个重载函数该被调用”(发生在编译期)和“哪一个virtual函数该被绑定”(发生在运行期)之间的差异。

2)显式接口由函数的签名式(也就是函数名称、参数类型、返回类型)构成。隐式接口就完全不同了。它并不基于函数签名式,而是由有效表达式(valid expression)组成。

 

 

条款42:了解typename的双重意义(Understand the two meanings of typename.

1)template内出现的名称如果相依于某个template参数,称之为从属名称(dependent names)。如果从属名称在class内呈嵌套状,称之为嵌套从属名称(nested dependent name)。

一般性规则:任何时候当你想要在template中指涉一个嵌套从属类型名称,就必须在紧临它的前一个位置放上关键字typename

 

 

这个一般性规则的例外是,typename不可以出现在base classes list内的嵌套从属类型名称之前,也不可在member initialization list(成员初始列)中作为base class修饰符。

 

2)声明template参数时,前缀关键字classtypename可互换。

 

条款43:学习处理模板化基类内的名称(Know how to access names in templatized base classes.

1)模板全特化(total template specialization):template MsgSender针对类型CompanyZoom特化了,而且其特化是全面性的,也就是说一旦类型参数被定义为CompanyZoom,再没有其他template参数可供变化。

 

注意class定义式最前头的“template<>”语法象征这既不是template也不是标准class,而是个特化版的MsgSender template,在template实参是CompanyZ时被使用。

2)当编译器遭遇template derived class定义式时,并不知道它继承什么样的class。如果在template derived class内部函数中调用了template base class的函数,编译器不到最后(当template derived class被具现化)并不知道base classtemplate参数是什么,如果不知道template参数是什么,就无法知道base class看起来像什么,也就是没办法知道它base class是否有那个derived class想调用的函数。

3)令C++“不进入templatized base classes观察”的行为失效的办法:

(1)在base class函数调用动作之前加上“this->”。

 

(2)使用using声明式。

 

这里使用using的原因,编译器不进入base class作用域内查找,于是通过using告诉它,请它那么做。

(3)指出被调用的函数位于base class内。

 

 

条款44:将与参数无关的代码抽离templatesFactor parameter-independent code out of templates.

1)Templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。

2)因非类型模板参数(non-type template parameters)而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换template参数。

3)因类型参数(type parameters)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(binary representation)的具现类型(instantiation types)共享实现码。

 

条款45:运用成员函数模板接受所有兼容类型(Use member function templates to accept “all compatible types.”)

1)Templates和泛型编程(Generic Programming

 

对任何类型T和任何类型U,可以根据SmartPtr<U>生成一个SmartPtr<T>——因为SmartPtr<T>有个构造函数接受一个SmartPtr<U>参数。这一类构造函数根据对象u创建对象t,而ut的类型是同一个template的不同具现体,称之为泛化(generalizedcopy构造函数。

 

上述代码使用成员初值列(member initialization list)来初始化SmartPtr<T>之内类型为T*的成员变量,并以类型为U*的指针(由SmartPtr<U>持有)作为初值。这种行为只有当“存在某个隐式转换可将一个U*指针转为一个T*指针”时才能通过编译。

2)请使用member function templates(成员函数模板)生成“可接受所有兼容类型”的函数。

3)如果你声明member templates用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数和copy assignment操作符。

 

条款46:需要类型转换时请为模板定义非成员函数(Define non-member functions inside templates when type conversions are desired.

 

 

上述代码编译无法通过的原因是:在template实参推导过程中并不考虑采纳“通过构造函数而发生的”隐式类型转换,因而无法使用Rational<int>non-explicit构造函数将2转换为Rational<int>,进而将T推导为int

上述问题的解决方案:template class内的friend声明式可以指涉某个特定函数。

 

 

现在对operator*的混合式调用可以通过编译了,因为当对象oneHalf被声明为一个Rational<int>class Rational<int>于是被具现化出来,而作为过程的一部分,friend函数operator*(接受Rational<int>参数)也就被自动声明出来。后者身为一个函数而非函数模板(function template),因此编译器可在调用它时使用隐式转换函数(例如Rationalnon-explicit构造函数)。

上述代码虽然通过编译,却无法连接。最简单的可行办法是将operator*函数本体合并至声明式内:

 

“令friend函数调用class外部的辅助函数”可以优化代码,因为定义于class内的friend函数也暗自称为inline

总结:当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。

 

条款47:请使用traits classes表现类型信息(Use traits classes for information about types.

1STL共有5中迭代器分类:

(1)Input迭代器只能向前移动,一次一步,客户只可读取(不能涂写)它们所指的东西,而且只能读取一次。它们模仿指向输入文件的阅读指针(read pointer),C++程序库中的istream_iterators是这一分类的代表。

(2)Output迭代器情况类似,但一切只为输出:它们只向前移动,一次一步,客户只可涂写它们所指的东西,而且只能涂写一次。它们模仿指向输出文件的涂写指针(write pointer),ostream_iterators是这一分类的代表。

(3)forward迭代器,这种迭代器可以做前述两种分类所能做的每一件事,而且可以读或写其所指物一次以上。这使得它们可施行于多次性操作算法(multi-pass algorithms)。

(4)Bidirectional迭代器:它除了可以向前移动,还可以向后移动。STLlist迭代器就属于这一分类,setmultisetmapmultimap的迭代器也都是这一分类。

(5)randomaccess迭代器:它可以执行“迭代器算术”,也就是它可以在常量时间内向前或向后跳跃任意距离。vectordequestring提供的迭代器都是这一分类。

C++标准程序库提供的专属的卷标结构(tag struct)如下所示:

 

2)traits是一种技术,也是一个C++程序员共同遵守的协议。这个技术的要求之一是,它对内置(built-in)类型和用户自定义(user-define)类型的表现必须一样好。“traits必须能够施行于内置类型”意味“类型内的嵌套信息(nesting information)”这种东西出局了,因为我们无法将信息嵌套于原始指针内。因此类型的traits信息必须位于类型自身之外。标准技术是把它放进一个template及其一或多个特化版本中。

如何设计并实现一个traits class的方法如下:

 

 

 

 

STL中有一个名为advance函数,用来将某个迭代器移动某个给定距离。

 

使用traits技术就可以将advance用于内置类型,:

 

上述代码中iterator_traits<IterT>::iterator_category在编译器间确定,而if语句却在运行期才会核定。解决这个问题的办法是重载(overloading)。

 

 

如何使用一个traits class的总结:

 

3)Traits classes使得“类型相关信息”在编译期可用。它们以templates和“templates特化”完成实现。

4)整个重载技术(overloading)后,traits classes有可能在编译期对类型执行if...else测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值