[C++从进阶到入门] —— 迭代器特性萃取机

1,017 阅读2分钟

参数推导

考虑如何使用C++实现下面python的代码?即定义一个和变量a类型相同的变量b?

>>> a = int(1)
>>> type(a)
<type 'int'>
>>> b = '2'
>>> type(b)
<type 'str'>
>>> c = type(a)(b)
>>> c
2
>>> type(c)
<type 'int'>

RTTI性质中的typeid()只能够获取类型名称,无法像python中的type()一样用作变量定义。解决办法是使用function template的参数推导机制。

template <class T>
void type_impl(T t) {
    T tmp = 1;
    cout << tmp << endl; // 1
}

int main() {
    int i = 0;
    type_impl(i);
}

上述代码的缺点很明显,函数模板无法推导函数返回类型。

内嵌型别

迭代器所指型别称为value_type,如果value_type需要用于函数返回值,参数推导无法实现。解决办法是使用内嵌型别。

template <class T>
struct Iter {
    typedef T value_type;
    T* ptr;
    Iter(T* p = 0):ptr(p) {}
    T& operator*() const {
        return *ptr;
    }
};

template <class I>
typename I::value_type func(I iter) {
	// typename通知编译器I::value_type是一个型别
    return *iter;
}

int main() {
    Iter<int> iter(new int(1));
    cout << func(iter) << endl;	// 1
}

上述代码也有缺点,比如对于原生指针,是没有value_type内嵌的。

偏特化

偏特化(partial specialization):对于一个以上的模板参数,我们可以针对某个或某些模板参数进行特化工作,也就是将泛化版本中的某些template参数赋予明确的指定。

例如对于:

template <class T>
class C {...};	// 该泛化版本可以允许任意型别

可以有如下偏特化版本。

template <class T>
class C<T*> {...};	// 该特化版本仅允许原生指针型别

有了这个方法,就可以解决原生指针无法内嵌型别的问题。

萃取机

下面的class template专门用来萃取迭代器的特性,value_type正是迭代器的特性之一。

template <class I>
struct iterator_traits {
	typedef typename I::value_type value_type;
};

上述代码的意义是:如果I定义有自己value_type,就可以萃取出。

于是我们可以这样使用:

template <class T>
struct Iter {
    typedef T value_type;
    T* ptr;
    Iter(T* p = 0):ptr(p) {}
    T& operator*() const {
        return *ptr;
    }
};

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

int main() {
    Iter<int> iter(new int(1));
    cout << func(iter) << endl;	// 1
}

多了这一中间层带来的好处是traits可以拥有特化版本。

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

于是即便原生指针没有内嵌型别,也可以通过traits取其value_type。

需要注意的是对于iterator_traits<const int*>::value_type取出的是const int,如果想要取出int,我们需要另一个特化版本。

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

到此为止,无论是迭代器,还是原生指针都可以通过traits取出正确的value_type。

STL约定了5种内嵌型别,我们只有遵守这个约定,才可以兼容STL。

template <class I>
struct iterator_traits {
	typedef typename I::iterator_category iterator_category;
    typedef typename I::value_type value_type;
    typedef typename I::difference_type difference_type;
    typedef typename I::pointer pointer;
    typedef typename I::reference reference;
};

本文内容参考 —— 《STL源码剖析》