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++编译期间的多态”。
最后
如果你有疑惑,欢迎评论,我会尽可能回复!
如果本文对你有帮助,点个赞吧,这是我坚持的动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!