@原创文章,转载请注明: 转载自 镜中影的技术博客
本文链接地址: traits编程技法感悟)
URL:http://blog.csdn.net/linkpark1904/article/details/50790917
由于工作学习需要,在研究生阶段主要编程语言还是以C++为主,多少在学习语言的过程中,道听途说c++很高大上,c++是一门四不像的语言,c++学起来很难,用c++做工程的不多等等这之类的话。于一门语言来说,c++确实算的上历史悠久,众人对其评价也是褒贬不一。无论如何,在我看来,一门语言能存活至今,肯定有其存在的价值和意义,也有许多值得我们学习的地方。
近来有幸拜读《STL源码剖析》,膜拜一下设计标准C++库的大神们是怎样用C++来绘制出STL这个庞大壮丽的王国。
书中偶然发现STL中是如何设计迭代器(所谓迭代器,本质上就是通用智能指针)的,书中提及到一种Traits编程技法,感觉很新奇。所谓Traits编程技法,目的是为了弥补C++没有typeof()调用的缺陷。在脚本语言(javascript,python……)中,变量的类型往往可以通过typeof调用获取,而在C++中这是没法做到的,所以才会采用一些特殊的办法去弥补这一点。
回归正题,倘若让你设计一个通用的迭代器,以及一个能打印任何通用迭代器的值得模板函数,你会怎么设计?所谓指针无非是提供*操作(暂时不考虑指针的++,–操作)。所以第一款自定义迭代器的设计如下所示:
template <class T>
class MyIter{
public:
typedef T value_type;
T *ptr;
MyIter(T *p = 0):ptr(p){}
T& operator*()const{return *ptr;}
};
其中利用typedef T value_type
把模板类型T暴露出来。这个指针类,提供了构造函数,重载了*操作符,如何设计打印通用迭代器值得函数呢?这个函数的返回值又怎么来定?正确的做法如下:
template <class I>
typename I::value_type
func(I iter){
return *iter;
}
其中,模板I必须为提供value_type变量的自定义迭代器,编译器在编译阶段,会通过MyIter的实际模板值推导出func函数的返回值类型应该是什么,这样func函数的返回值就可以随着我们自定义迭代器传入的模板参数的改变而改变。说白了,如果我们给MyIter传入一个int型,那么由于typedef T value_type
的作用,导致value_type为int,所以func的返回值为int,整体的可执行代码如下:
#include <iostream>
using namespace std;
template <class T>
class MyIter{
public:
typedef T value_type;
T *ptr;
MyIter(T *p = 0):ptr(p){}
T& operator*()const{return *ptr;}
};
template <class I>
typename I::value_type
func(I iter){
return *iter;
}
int main(){
MyIter<int> myIter(new int(4));
cout << func(myIter) <<endl;
return 0;
}
不要以为这样就完美了,这里我们的func函数只能支持我们自定义的迭代器,也就是迭代器中自定义value_type那么对于一个通用指针如何进行支持呢,例如func(new int(4))
这样的调用,为了让函数好用,做到极致就需要函数能够适配多种多样的情况。为此才会引申出我们的Traits编程技法。利用模板的特化(有点像函数的重载)特性,在中间增加一层Traits类,让我们设计的func函数既支持自定义的迭代器又支持通用指针。具体代码如下所示:
#include <iostream>
using namespace std;
//自定义迭代器
template <class T>
struct MyIter{
typedef T value_type;
T *ptr;
MyIter(T* p = 0):ptr(p){}
T& operator*() const{ return *ptr;}
};
//Traits编程技法
//支持自定义迭代器
template <class T>
class Traits{
public:
typedef typename T::value_type value_type;
};
//特化,支持传统通用指针
template <class T>
class Traits<T*>{
public:
typedef T value_type;
};
//特化,支持传统const指针
template <class T>
class Traits<const T*>{
public:
typedef T value_type;
};
//模板函数
template <class I>
typename Traits<I>::value_type
func(I iter){
return *iter;
}
int main(){
MyIter< int > p(new int(8));
const char *ptr = "abce";
int *a = new int(9);
cout << func(p) <<endl;
cout << func(a) <<endl;
cout << func(ptr) << endl;
return 0;
}
同样也是利用编译器在编译的时候进行类型的推导,不同于第一个版本,这次推导在Traits这一层做了特化,针对不同类型的指针类型,做不同的操作,大概就是这样一种思想吧。