篮子、水果和鸡蛋——关于C++的模板偏特化和萃取编程技法

江枫思渺然 提交于 2019-11-29 01:48:52

最近在读《STL源码剖析》。读这本书的时候发现自己的C++的知识其实是非常匮乏的。 从大学的C++教材上学到一些C++基本的语法、内存管理、继承、多态等方面的基础知识。这些只是是一棵大树的根。而读STL的源码和侯捷的解析的时候,发现C++还有很多丰富的细节和技巧。这些是大树上的枝叶。学习C++,不仅要学习根,也要学习枝叶,这样才能让大树茂盛起来。虽然C++语法一些用法较为晦涩,但读完这些代码之后觉得思路比以前更开阔,另外可以活动脑筋。

比如模板的偏特化这个特性。侯捷的《STL源码剖析》中对于模板的偏特化(partial specialization)的解释为: 如果class template拥有一个以上的template参数,我们可以针对其中某个或多个 template参数进行特化工作。template是一个很抽象的东西。template偏特化之后就让模板变得具体那么一点点。

用一个形象一点的比喻吧。我们把template比作一个装东西的篮子。这个篮子既可以装鸡蛋,也可以装苹果。那么所谓偏特化就是让你用一个篮子专门装水果,这就是template水果篮。以后你就只能使用水果篮来装苹果、装梨,而不能使用其他的篮子来装这些水果了。

看一个STL例子:

有一个“篮子” iterator_traits, 它内部typedef 了value_type类型,用来定义模板的参数类型class I 的value_type。

template<class I>
struct iterator_traits
{
    typedef typename I::value_type value_type;
};
如果用一般的STL的iterator类型去实例化这个模板的时候是不会有问题的,因为STL内建的iterator里typedef了value_type。但是如果用原生指针去实例化这个模板就悲剧了。因为原生指针可没有办法typedef什么东西。如果把iterator比作鸡蛋,把原生指针比作水果,那么这个篮子就只能装鸡蛋而不能装水果。如果必须要装水果,怎么办?


办法就是用模板的偏特化,给水果专门做一个水果篮了。看下面的代码

template <class T>
struct iterator_traits<T*>
{
typedef T value_type;
}

当用原生指针T*去实例化iterator_traits模板的时候,就会调用上面的代码把类型T typedef为value_type。这就相当于给iterator_traits实现了一个特别的版本专门处理原生指针。也就是说,水果篮做好了:)

如果理解了模板的偏特化, 就理解了萃取编程技法的一大半了。什么是萃取编程技法呢? 萃取来自于英语的traits, traits的意思是特性、特点。萃取编程技法实际上就是利用模板的偏特化,充分的萃取不同型别的特点。

还是拿鸡蛋、水果和篮子做例子。别看装鸡蛋、水果都用篮子,但是装鸡蛋的篮子和装水果的篮子能一样吗? 装鸡蛋的篮子一定要抗摔防震。因为鸡蛋壳很脆一碰就破。而装水果的篮子则要美观好看。因为水果篮一般都用作送给别人的礼物。

还是用上面的iterator_traits 这个篮子做例子,看下面的代码。

template<class I>
struct iterator_traits
{
    typedef typename I::value_type value_type;
}; //泛化版本

template<class I>
struct iterator_traits<T*>
{
    typedef T value_type;
}; //偏特化版本

我们给这个iterator_traits设计了两个版本。上面的是泛化的版本,下面的是为原生指针偏特化的版本。

如何使用iterator_traits这个篮子呢,看下面这个模板函数:

template <class I>
typename iterator_traits<I>::value_type 
func(I ite)
{
    return *ite;
}

这个模板函数以一个class I作为类型参数, 函数的参数为一个class I类型的对象, 返回的是class I对象提领的对象。函数的返回值类型定义就使用了萃取编程的技法。我们看函数返回值的定义:

iterator_traits<I>

这个模板就是上文中我们定义的篮子。在上文中我们针对不同类型的特点,实现了两种将template的实例化的方法:实现了用STL的iterator去实例化这个模板的方法,也实现了用原生指针去实例化这个模板的方法。因为我们“因材施教”的调用了不同的代码,才把水果放在了水果篮里,把鸡蛋放在其他的篮子里。这就是萃取的编程技法。


C++的语法是很艰深的。STL对于C++的使用也可以说是很有“创造性”。 因为这种“创造性”,给我们阅读STL源码的时候造成了一定的困难,不过也是因为这种“创造性” , 让我们使用STL的时候非常的轻松和愉快。哎,这就是所谓的“痛并且快乐着”的那种感觉吧。





易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!