类型萃取

May 9, 2013


STL中有五种迭代器:
input_iterator,
output_iterator,
forward_iterator,
bidirectional_iterator,
random_access_iterator,

迭代器最主要的好处时,不会暴露你放在在容器中的对象的具体细节。也就是说,当你有一个迭代器,返回了你想要的对象。但是在使用过程中有没有疑问,就是由于容器都是模板,迭代器隐藏了放置在容器中的对象的具体细节。那迭代器又是怎么知道我想要的那个对象恰好是对的那个类型呢?还有就是,STL中大量使用了模板,某些工具性的函数返回的都是未知的类型(出于隐藏内部对象的细节的考虑),那我怎么知道这个函数会返回什么类型呢?在C++模板中,模板参数推导机制能有效的推导出参数的类型,但是问题是,我如何推导出函数返回类型呢?出于隐藏客户对象的细节的考虑,肯定是不能直接返回对象的类型的,这种情况下,迭代器如何做到的?

答案就在于萃取。萃取:就是萃取出迭代器的类型。例如:

std::vector<int>::iterator iter;

通过萃取就可以得到iter的返回类型int(当然,这里有点牵强,因为vector中的迭代器实际上就是 value_type*)。在STL中,自行定义内嵌类型的方式自定义出相应类型,通过自定义的类型进行类型推导,这样以达到得到返回对象的类型。

关于迭代器的实现可以翻阅STL的源码,这里不作赘述,这里主要用一个傻逼的模拟说明萃取的实现过程。STL中类型特性一般有:对象,引用,指针,const引用,const指针等。用一个结构体描述萃取:

template<typename T>  // T -- People<int>  
struct my_iterator_traits  
{  
    typedef typename T::value_type value_type;  // People<int> data member value_type  
    typedef typename T::point point;  
    typedef typename T::reference reference;  
};  

然后是一个模板类(此类没有太多的意义):

template<typename T> // int----------2  
class People  
{  
public:  
    typedef T value_type;  
    typedef T* point;  
    typedef T& reference;  
public:  
    T* ptr; // T -- int  
    People(T* p = 0):ptr(p)  // T -- int  
    {  
        std::cout << "construct People!" << endl;  
    }  
    T& operator*()  
    {  
        return *ptr;  
    }  
    T* operator&()  
    {  
        return ptr;  
    }  
private:  
    int p;  
};  

使用者:

template<typename T> // T -- People<int>  
inline typename my_iterator_traits<T>::value_type  //  People<int> data member  
myFun(T i)  //使用者 T -- People<int>  
{  
    return *i; // invoke operator*,return *ptr  
}

main函数:

int _tmain(int argc, _TCHAR* argv[])  
{  
    People<int>me(new int(8));    //------------------ 1  
    std::cout << myFun(me) << endl;  
    return 0;  
}  

运行打印出:8 下面解析一下程序。my_iterator_traits结构体,使用typedef将模板参数T中的型别别名化了。1定义了一个对象me(模板参数类型int),其成员变量p被初始化成指向8的一个整形变量。接下来调用myFun调用者,myFun中,T为People<int>,myFun的返回值是:typename my_iterator_traits<T>::value_type,这里的T是People<int>,转而进去People内部看看有typedef T value_type,这是一个People的内嵌类型,由于此例中People的模板参数类型是int,那么typedef替换之后实际上就是 typedef int value

那么在 my_iterator_traits结构中,访问了People的内嵌类型,也做了别名化,但是实际程序在编译阶段已经将 typedef typename T::value_type value_type替换成typedef typename People<int>::value_type value_type,也就说myFun函数实际上返回的是int类型,People中重载了*操作符,返回的是ptr所指向的值。这整个过程就完成了迭代器的一个萃取过程,myFun使用者成功萃取出了所需要返回的类型。

这里可能有人说,何必这么麻烦,要返回的int类型不就可以了?但是有必要说明的是,这里我们只是以最简单的C++内建数据类型测试了,如果myFun是STL中内置函数,而People的模板参数不是内建类型而是自定义类类型,那这样的myFun应该返回哪种类型呢?

这个例程里面还少了一个比较重要的地方,就是模板特化,当传回的是原生指针呢,所有应该有一个萃取器对原生指针的特化这里就不用多说了,每一本将泛型或者STL的书中都会讲这个的,自己也可以去翻阅STL的源码。