迭代器偏移——advance

2023-10-08 20:30
文章标签 偏移 迭代 advance

本文主要是介绍迭代器偏移——advance,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 场景

业务场景使用unordered_map,每次从指定位置n(n不大于unordered_map长度)开始往后遍历。由于unordered_map没有operator+=不能像vector一样通过+=偏移到指定位置。advance可以将迭代器偏移到n个位置。
本文简单探究一下advance不同迭代器实现过程是怎样,由于advance实现依赖迭代器类型,本文还会简单介绍一下iterator_category。

2. advance 用法介绍

http://www.cplusplus.com/reference/iterator/advance/?kw=advance

Advance iterator
template <class InputIterator, class Distance>void advance (InputIterator& it, Distance n);

Advances the iterator it by n element positions.
If it is a random-access iterator, the function uses just once operator+ or operator-. Otherwise, the function uses repeatedly the increase or decrease operator (operator++ or operator–) until n elements have been advanced.

Parameters

it
  Iterator to be advanced.
  InputIterator shall be at least an input iterator.
n
  Number of element positions to advance.
  This shall only be negative for random-access and bidirectional iterators.
  Distance shall be a numerical type able to represent distances between iterators of this type.

Return value

none

3. advance源码分析

3.1 源码查看
  /***  @brief A generalization of pointer arithmetic.*  @param  i  An input iterator.*  @param  n  The "delta" by which to change @p i.*  @return  Nothing.**  This increments @p i by @p n.  For bidirectional and random access*  iterators, @p n may be negative, in which case @p i is decremented.**  For random access iterators, this uses their @c + and @c - operations*  and are constant time.  For other %iterator classes they are linear time.*/template<typename _InputIterator, typename _Distance>inline voidadvance(_InputIterator& __i, _Distance __n){// concept requirements -- taken care of in __advancetypename iterator_traits<_InputIterator>::difference_type __d = __n;std::__advance(__i, __d, std::__iterator_category(__i));}

__advance 有两个特化

  template<typename _InputIterator, typename _Distance>inline void__advance(_InputIterator& __i, _Distance __n, input_iterator_tag){// concept requirements__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)while (__n--)++__i;}template<typename _BidirectionalIterator, typename _Distance>inline void__advance(_BidirectionalIterator& __i, _Distance __n,bidirectional_iterator_tag){// concept requirements__glibcxx_function_requires(_BidirectionalIteratorConcept<_BidirectionalIterator>)if (__n > 0)while (__n--)++__i;elsewhile (__n++)--__i;}template<typename _RandomAccessIterator, typename _Distance>inline void__advance(_RandomAccessIterator& __i, _Distance __n,random_access_iterator_tag){// concept requirements__glibcxx_function_requires(_RandomAccessIteratorConcept<_RandomAccessIterator>)__i += __n;}

从源码上可知,调用哪一个__advance 取决于 iterator_category 类型。

4. 迭代器类型 iterator_category

一共有5种iterator_category分别为:

  1. input_iterator:istream独有的迭代器。
  2. output_iterator:ostream独有的迭代器。
  3. forward_iterator:继承自input_iterator,单向走的迭代器,只能走一个,不能跳。如forward_list、单向list的hashtable
  4. bidirectional_iterator:继承自forward_iterator,双向走的迭代器,只能走一个,不能跳。如list、rb-tree、双向list的hashtable
  5. random_access_iterator:继承自bidirectional_iterator,可以跳的迭代器。如array、vector、deque。

使用stl我们一般不需要关系迭代器类型,但在stl源码中有很多地方使用到,这里我们只针在advance中迭代器类型起到的作用。

4.1 迭代器类型定义
  /***  @defgroup iterators Iterators*  These are empty types, used to distinguish different iterators.  The*  distinction is not made by what they contain, but simply by what they*  are.  Different underlying algorithms can then be used based on the*  different operations supported by different iterator types.*///@{ ///  Marking input iterators.struct input_iterator_tag { };///  Marking output iterators.struct output_iterator_tag { };/// Forward iterators support a superset of input iterator operations.struct forward_iterator_tag : public input_iterator_tag { };/// Bidirectional iterators support a superset of forward iterator/// operations.struct bidirectional_iterator_tag : public forward_iterator_tag { };/// Random-access iterators support a superset of bidirectional iterator/// operations.struct random_access_iterator_tag : public bidirectional_iterator_tag { };
4.2 几种容器迭代器类型定义
unorder_map迭代器类型:forward_iterator_tag

在这里插入图片描述

map迭代器类型:bidirectional_iterator_tag

在这里插入图片描述

vector迭代器类型:bidirectional_iterator_tag

vector 使用的是通用迭代器:random_access_iterator_tag
在这里插入图片描述

  /***  This class does nothing but define nested typedefs.  The general*  version simply "forwards" the nested typedefs from the Iterator*  argument.  Specialized versions for pointers and pointers-to-const*  provide tighter, more correct semantics.*/template<typename _Iterator>struct iterator_traits{typedef typename _Iterator::iterator_category iterator_category;typedef typename _Iterator::value_type        value_type;typedef typename _Iterator::difference_type   difference_type;typedef typename _Iterator::pointer           pointer;typedef typename _Iterator::reference         reference;};template<typename _Tp>struct iterator_traits<_Tp*>{typedef random_access_iterator_tag iterator_category;typedef _Tp                         value_type;typedef ptrdiff_t                   difference_type;typedef _Tp*                        pointer;typedef _Tp&                        reference;};template<typename _Tp>struct iterator_traits<const _Tp*>{typedef random_access_iterator_tag iterator_category;typedef _Tp                         value_type;typedef ptrdiff_t                   difference_type;typedef const _Tp*                  pointer;typedef const _Tp&                  reference;};

5. 例子

#include <iostream>
#include <unordered_map>
#include <vector>
#include <map>
using namespace std;// g++ advance.cpp  -std=c++0x -o aa
void test_umap()
{unordered_map<int, int> umap;int n1 = 100;for (size_t i = 0; i < n1; i++){umap[i] = i+1000000;}cout <<"test_umap bucket count: "<<umap.bucket_count()<<endl;// 表格重建for (size_t i = n1; i < n1 + 100000; i++){umap[i] = i+1000000;}cout <<"test_umap bucket count: "<<umap.bucket_count()<<endl;auto it = umap.begin();advance(it, 5);for(int i = 0; i < 5; ++i, ++it){cout<<it->first<<", "<<it->second<<endl;}
}void test_map()
{map<int, int> mp;for (size_t i = 0; i < 100; i++){mp[i] = i+1000000;}auto it = mp.begin();advance(it, 5);cout<<"\ntest_map"<<endl;for(int i = 0; i < 5; ++i, ++it){cout<<it->first<<", "<<it->second<<endl;}
}void test_vector()
{vector<int> vt;for (size_t i = 0; i < 100; i++){vt.push_back(i+1000000);}auto it = vt.begin();advance(it, 5);cout<<"\ntest_vector"<<endl;for(int i = 0; i < 5; ++i, ++it){cout<<*it<<endl;}
}int main()
{test_umap();test_map();test_vector();cout <<"\nmain end"<<endl;
}

运行结果

test_umap bucket count: 199
test_umap bucket count: 126271
5, 1000005
6, 1000006
7, 1000007
8, 1000008
9, 1000009test_map
5, 1000005
6, 1000006
7, 1000007
8, 1000008
9, 1000009test_vector
1000005
1000006
1000007
1000008
1000009main end

在这里插入图片描述

这篇关于迭代器偏移——advance的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/167808

相关文章

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

Mybatis从3.4.0版本到3.5.7版本的迭代方法实现

《Mybatis从3.4.0版本到3.5.7版本的迭代方法实现》本文主要介绍了Mybatis从3.4.0版本到3.5.7版本的迭代方法实现,包括主要的功能增强、不兼容的更改和修复的错误,具有一定的参考... 目录一、3.4.01、主要的功能增强2、selectCursor example3、不兼容的更改二、

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

计算数组的斜率,偏移,R2

模拟Excel中的R2的计算。         public bool fnCheckRear_R2(List<double[]> lRear, int iMinRear, int iMaxRear, ref double dR2)         {             bool bResult = true;             int n = 0;             dou

多线程篇(阻塞队列- LinkedBlockingDeque)(持续更新迭代)

目录 一、LinkedBlockingDeque是什么 二、核心属性详解 三、核心方法详解 addFirst(E e) offerFirst(E e) putFirst(E e) removeFirst() pollFirst() takeFirst() 其他 四、总结 一、LinkedBlockingDeque是什么 首先queue是一种数据结构,一个集合中

多线程篇(阻塞队列- LinkedBlockingQueue)(持续更新迭代)

目录 一、基本概要 1. 构造函数 2. 内部成员 二、非阻塞式添加元素:add、offer方法原理 offer的实现 enqueue入队操作 signalNotEmpty唤醒 删除线程(如消费者线程) 为什么要判断if (c == 0)时才去唤醒消费线程呢? 三、阻塞式添加元素:put 方法原理 图解:put线程的阻塞过程 四、非阻塞式移除:poll方法原理 dequ

六、我们应当怎样做需求调研:迭代

前面我一直在反复强调这样一个观点,需求分析不是一蹴而就的,是一个反复迭代的过程。它将从第一次需求分析开始,一直持续到整个项目生命周期。为什么这样说呢?让我们一起来分析分析。  在第一次的需求分析阶段,我们在一段时期内需要与客户进行反复地讨论,这个过程往往是这样一个反复循环的过程:需求捕获->需求整理->需求验证->再需求捕获••••••  需求捕获,就是我们与客户在一起开研讨会

多线程篇(阻塞队列- ArrayBlockingQueue)(持续更新迭代)

目录 一、源码分析 1. 先看个关系图 2. 构造方法 3. 核心属性 4. 核心功能 入队(放入数据) 出队(取出数据) 5. 总结 一、源码分析 1. 先看个关系图 PS:先看个关系图 ArrayBlockingQueue是最典型的有界阻塞队列,其内部是用数组存储元素的, 初始化时需要指定容量大小利用 ReentrantLock 实现线程安全。 在生产者

多线程篇(并发相关类- 原子操作类)(持续更新迭代)

目录 前言 一、原子变量操作类(AtomicLong为例) 1. 前言 2. 实例 二、JDK 8新增的原子操作类LongAdder 三、LongAccumulator类原理探究 前言 JUC包提供了一系列的原子性操作类,这些类都是使用非阻塞算法CAS实现的,相比使用锁实现原子性操作这在性能上有很大提高。 由于原子性操作类的原理都大致相同,这里讲解最简单的AtomicLo