作为泛型编程的一个简单例子,让我们看一下在C库中如何让memcpy()函数泛型化。一种实现方法可能是这样的:
void*memcpy(void*region1,constvoid*region2,size_tn){constchar*first=(constchar*)region2;constchar*last=((constchar*)region2)+n;char*result=(char*)region1;while(first!=last)*result++=*first++;returnresult;}这个memcpy()函数已经在一定程度上进行了泛型化,采取的措施是使用void*,这样该函数就可以拷贝不同类型的数组。但如果我们想拷贝的数据不是放在数组里,而是由链表来存放呢?我们能不能扩展这个概念,让它可以拷贝任意的序列?看看memcpy()的函数体,这个函数对传入的序列有一个_最小需求_:它需要用某种形式的指针来遍历这个序列;访问指针正指向的元素;把元素写到目的地;比较指针以判断何时停止拷贝。C++标准库把这样的需求进行分组,称之为概念(concepts)。在这个例子中就有输入迭代器(对应于region1)和输出迭代器(对应于region2)这两个概念。
如果我们把memcpy()用函数模板重写,使用输入迭代器和输出迭代器作为模板参数来描述对序列的需求,我们就可以写出一个具有较高重用性的copy()函数:
template
template
标记分派和traits类的联系很紧密。分派所依据的属性(在这个例子中是iterator_category)一般都是通过traits类来取得。主advance()函数从iterator_traits中获得对应于该iterator的iterator_category,然后调用重载过的advance_dispach()函数。编译器依据作为参数传给advance_dispach()的iterator_category,选择合适的重载版本。标记只是一个极其简单的类,它的唯一任务就是为标记分派或者其它类似技术传递必要的信息。
namespacestd{structinput_iterator_tag{};structbidirectional_iterator_tag{};structrandom_access_iterator_tag{};namespacedetail{template
类型生成器的工作是依据它的模板参数合成新的类型[注]。类型生成器产生的新类型一般作为生成器中嵌套的typedef出现。用类型生成器的主要目的是为了让复杂的类型表达式显得更简单些。比如boost::filter_iterator_generator:
template
boost::filter_iterator_generator
对象生成器是一种函数模板,依据其参数产生新的对象。可以把它想象成泛型化的构造函数。有些情况下,欲生成的对象的精确类型很难甚至根本无法表示出来,这时对象生成器可就管用了。对象生成器的优点还在于它的返回值可以直接作为函数参数,而不像构造函数那样只有在定义变量时才会调用。Boost和标准库中用到的对象生成器大多都加了个前缀make_,比如std::make_pair(constT&,constU&)。
看看下面的例子:
structwidget{voidtweak(int);};std::vector
voidtweak_all_widgets1(intarg){for_each(widget_ptrs.begin(),widget_ptrs.end(),bind2nd(std::mem_fun(&widget::tweak),arg));}如果不用对象生成器,上面的函数可能就得这样来实现:
voidtweak_all_widgets2(intarg){for_each(struct_ptrs.begin(),struct_ptrs.end(),std::binder2nd
策略类就是用来传递行为的模板参数。标准库中的std::allocator就是策略类,把内存管理的行为应用到标准容器中。
“策略类精确地反映了设计时的抉择。他们要么从其它类中继承,要么包含在其它类中。策略类在同样的句法结构上提供了不同的策略。使用策略的类把它用到的每一个策略都作为模板参数,这样,用户就可以自由地选择需要使用的策略。
策略类的强大在于它们能够自由地组合在一起。通过把策略类作为模板参数的办法来组合多种策略,代码量与使用的策略数只成线性关系。”
Andrei认为策略类的强大源于其小粒度和正交性。Boost在迭代适配器库中的策略类运用可能淡化了这一卖点。在这个库中,所有已适配的迭代器的行为都放在一个策略类里面。其实,Boost并不是开先河者。std::char_traits就是一个策略类,它决定了std::basic_string的行为,尽管它的名字叫traits而不叫policy。
注:因为C++缺少模板化的typedef,类型生成器可以作为其替代方案。