为了完成一个迭代器,我们往往会把容器实现的太多细节暴露在外面,于是干脆把迭代器的开发交予容器设计者,如此一来所有的实现细节便能不被使用者所见,实现了封装。
为了使泛型算法使用迭代器,可能会使用到相应型别(associated type),【这里指的是迭代器所指之物的型别】。而C++本身不支持typeof(),RTT的typeid()获得的也只是类型名称而不能拿来做变量声明。
解决办法:function template的参数推导(argument deduction)机制。(STL源码剖析page85)
参数推导机制可以解决迭代器的value type,但一旦valuetype必须用于函数的传回值变束手无策了(template参数推导机制推而导之的只是参数,无法推导函数的返回值型别)。
解决办法:声明内嵌类型(STL源码剖析page86)
但是存在一个隐晦的陷阱,不是所有的迭代器都是class type,比如原生指针,她无法定义为内嵌型别。
解决办法:针对特殊情况做特殊化处理,使用template partial specialization(类模板偏特化),
【附注】所谓偏特化不是指对模板参数的个别指定,而是提供另一份template,而其本身仍为templatized
正式进入trait技法。
value type是迭代器的特性之一,迭代器还拥有其他特性,故使用专门的类将其封装。统一作为迭代器型别的“榨汁机”-------iterator_traits(原生指针和const类型使用偏特化)
template<class I>
struct iterator_traits
{
typedef typename I::iterator_category iterator_category; //为了使编译器能够识别,使用typename声明其为一个型别
typedef typename I::value_type value_type; //而非class function member or data member
typedef typename I::different_type different_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
};
为了使traits可以正常使用, 每一个迭代器必须遵守约定,自行已内嵌型别定义的方式定义相应型别,否则自别与STL这个大家庭。
然后为原生指针和const类型偏特化(const“萃取”后为右值,特化其为萃取左值)【STL源码剖析page90-94】
在具体的算法里还需要进一步的封装【page93-99有例子】
一些零碎的笔记:
在C++中,函数如果要传回左值,都是已by reference的方式进行的。
在研究STL中,我们一定要谨记效率是个重要的课题。
命名规则:只在程序内部使用的函数加前导符(_advanced())
命名规则:STL算法所能接受的最低阶迭代器作为迭代器型别的参数命名
STL规范:任何迭代器都应该提供五个内嵌相应型别
总结:
设计适当的相应型别,是迭代器的责任,设计适当的迭代器,是容器的责任,,只有容器本身,才知道该设计怎样到迭代器遍历自己,执行迭代器该有的各种行为,至于算法,只要设计时以迭代器为对外接口就行。
traits利用内嵌型别的编程技巧与编译器的template参数推导功能弥补了C++未能提供的型别认证方面的能力。
参考资料:
《STL源码剖析》
《C++ primer》