traits和policy在泛型编程里面还是挺常见的。像stl的string实现里面就用到了traits,boost里面也很多地方用到traits。
traits和policy很多时候都会一起使用,让我们在泛型编程里面多了一些思路。
traits:中文解释为特征,记得候捷在《stl源码剖析》那本书里面还叫做萃取什么的。当我们想从一个类型身上获取他的一个附加特性的时候,往往可以考虑traits。比方说有一个类型T, 对他进行一些操作,然后要返回一个类型,那么可以理解为一个返回类型是跟T相关的。还有比如给定一个迭代器,想从迭代器上获取相关的容器类型什么。这个都是type traits的典型应用,至于value traits,很多时候是在一个traits里面给类型附加一个初始化值。经常可以看到一个tratis类里面有一个类型定义和一个相应的初始化(静态常量或者一个静态函数)。类似:
template<> struct traits<int> { typedef double AccuT; static AccuT Zero(){ return 0.0; }; };
policy:中文解释为策略。在泛型编程里面,如果某些行为代码可能会变化,那么往往可以使用policy。通过使用不同的policy可以改变一个模板函数的行为。比如下面的代码:
template<class T, class P> typename traits<T>::AccuT accum(const T* ptr, int len) { traits<T>::AccuT total = traits<T>::Zero(); for (int i = 0; i < len; i++) { P::accumulate(total, *(ptr + i)); } return total; }
如果累加算法可能会变,那么我们就可以通过传入一个模板类,通过这个类的成员函数来实现不同的算法。
traits和policy拓展了我们的视野,让我们多了一些思路。但是也不要一味的滥用(就像设计模式一样,有些时候过度设计还不如没有设计)。
stl的accumulate函数就没有使用traits和policy。看下面的代码:
template<class _InIt, class _Ty> inline _Ty accumulate(_InIt _First, _InIt _Last, _Ty _Val) { // return sum of _Val and all in [_First, _Last) _DEBUG_RANGE(_First, _Last); return (_Accumulate(_Unchecked(_First), _Unchecked(_Last), _Val)); }
template<class _InIt, class _Ty, class _Fn2> inline _Ty accumulate(_InIt _First, _InIt _Last, _Ty _Val, _Fn2 _Func) { // return sum of _Val and all in [_First, _Last), using _Func _DEBUG_RANGE(_First, _Last); _DEBUG_POINTER(_Func); return (_Accumulate(_Unchecked(_First), _Unchecked(_Last), _Val, _Func)); }
std::accumulate()的返回值类型其实是通过一个模板参数传进去的_Ty _Val。行为函数也是通过一个模板参数穿进去的_Fn2 _Func。
个人感觉每种方案都有自己的优缺点,还是那句话,在合适的场合使用合适的解决方案。方案本身没有绝对的好和不好,只有合适的方案没有最好的方案(只有最强的玩家没有最强的职业)
traits的原作者Nathan有句话:traits是一种替代模板参数的方案。但是他并没有说模板参数不好,或者traits比模板参数好多少。