iterator_traits技法

                <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                    <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                </svg>
                <center>

问题

在 C++ 泛型编程中,如何知道“迭代器所指对象的类型”,以便声明临时变量呢?我们把迭代器所指对象的类型称为value type

template <class Iterator>
void func(Iterator it)
{
    // 如果我想声明Iterator所指对象类型的临时变量应该怎么办呢?
    // 在这里没有办法!
}

函数模板的参数推导机制

但是可以利用函数模板的参数推导机制来达到目的。

template <class Iterator>
void func(Iterator it)
{
    func_impl(it, *it);		// 将任务交给func_impl完成
}

template <class Iterator, class T>
void func_impl(Iterator it, T t)
{
// 现在T就是Iterator所指对象的类型
T tmp; // 达到目的
}

这种以func函数为对外接口,实际任务由func_impl函数完成的机制称为分派机制。这种机制可以实现所谓的“C++编译期间的多态”,将在下一篇文章中讲述。

编译器会自然推导出 T 的类型,但是思考有一个新的问题“value type如何用于声明返回类型”?

迭代器的value type

函数模板的参数推导机制不能推断返回值的类型!怎么办呢?那就让迭代器告诉我们吧!

当实现一个Iterator类时,必须指明自己的value type是什么,比如:

template <class T>
struct MyIterator {
    typedef T value_type;	// 自己指定value type是什么
    .......
};

这样,在泛型编程时可以这样获取迭代器的value type

template <class Iterator>
typename Iterator::value_type	// 这一行都是返回类型声明
func(Iterator it)
{
    return *it; 
}

iterator_traits

接下来这个iterator_traits类模板专门用来“萃取”迭代器的特性,而value type正是迭代器特性之一。

template <class Iterator>
struct iterator_traits {
    typedef typename Iterator::value_type value_type;
    ......	// 其他更多特性
}

这个如果iterator_traits的作用是:如果Iterator本身有value type,那么就用它。

在这之后,func函数可以重新写成这样:

template <class Iterator>
typename iterator_traits<Iterator>::value_type	// 这一行都是返回类型声明
func(Iterator it)
{
    return *it; 
}

为什么要多加一层呢?假设每个迭代器的实现者都指定了自己的value type,但并不是每个迭代器都是class,能够通过域作用符访问里面的成员,比如原生指针int*

int main()
{
    int x = 888;
	func(&x);	// 这样是不可以的
}

偏特化

针对原生指针类型,可以使用偏特化技术,为其定制专门的版本。

template <class T>
struct iterator_traits<T*> {	// 针对原生指针T*的偏特化
    typedef T value_type;
    ......	// 其他更多特性
}

template <class T>
struct iterator_traits<const T> { // 针对原生指针const T的偏特化
typedef T value_type;
...... // 其他更多特性
}

原生指针可能还是const T*类型,为了正确推断出T而不是const T,需要为const T*偏特化一个版本。

总结

总之,iterator_traits这个类模板是一台“榨汁机”,专门“萃取”迭代器的各种特性。它能够正常工作的一个重要原因是,设计迭代器的人共同遵守一个约定:自行定义迭代器的相应类型。STL 大家庭都遵守了这个约定,不遵守这个约定就不能兼容这个大家庭。

迭代器的相应类型共有五种,完整的iterator_traits是这样的:

template <class Iterator>
struct iterator_traits {
    typedef typename Iterator::value_type 			value_type;			// 迭代器所指对象的类型
    typedef typename Iterator::iterator_category 	iterator_category;	// 迭代器的类别
    typedef typename Iterator::difference_type 		difference_type;	// 两个迭代器的之间距离
    typedef typename Iterator::pointer 				pointer				// 指向迭代器所指的对象
    typedef typename Iterator::reference 			reference			// 迭代器所指对象的引用
}

正是迭代器的 5 种类型和iterator_traits技法成就了所谓的“C++编译期间的多态”。

最后

如果你有疑惑,欢迎评论,我会尽可能回复!

如果本文对你有帮助,点个赞吧,这是我坚持的动力!

posted @ 2022-12-29 15:36  zwjason  阅读(29)  评论(0编辑  收藏  举报