获取std :: vector的迭代器索引的最有效方法是什么?

cai*_*rol 409 c++ iterator coding-style

我正在迭代一个向量,需要迭代器当前指向的索引.AFAIK这可以通过两种方式完成:

  • it - vec.begin()
  • std::distance(vec.begin(), it)

这些方法的优点和缺点是什么?

Unc*_*ens 522

我更倾向于it - vec.begin()出于Naveen给出的相反原因:如果将向量更改为列表,则无法编译.如果在每次迭代中执行此操作,您可能很容易将O(n)算法转换为O(n ^ 2)算法.

如果在迭代期间没有在容器中跳转,则另一个选项是将索引保持为第二个循环计数器.

注意:it是容器迭代器的通用名称std::container_type::iterator it;.

  • @Steinfeld是一个迭代器.`std :: container_type :: iterator it;` (32认同)
  • 到底是什么?它? (28认同)
  • 同意.我会说减号是最好的,但保留第二个循环计数器比使用std :: distance更好,因为这个函数可能很慢. (2认同)
  • 添加第二个循环计数器是一个显而易见的解决方案,令我很尴尬,我没想到。 (2认同)
  • @Swapnil,因为“ std :: list”不提供按元素位置直接访问元素的功能,因此,如果您不能执行“ list [5]”,则应该不能执行“ list.begin()+ 5”。 (2认同)

Nav*_*een 126

我更喜欢,std::distance(vec.begin(), it)因为它允许我更改容器而不需要任何代码更改.例如,如果您决定使用std::liststd::vector不是提供随机访问迭代器,则代码仍将编译.由于std :: distance根据迭代器特性选择最佳方法,因此您也不会有任何性能下降.

  • 当你使用没有随机访问迭代器的容器时,最好不要*计算这样的距离,因为它效率低下 (49认同)
  • @SteveJessop:有一个名为`vec`的向量也是个坏消息. (19认同)
  • 我认为如果容器发生变化,代码应该改变 - 有一个名为`vec`的std :: list变量是坏消息.如果代码被重写为通用代码,将容器类型作为模板参数,那就是我们可以(并且应该)讨论处理非随机访问迭代器的问题;-) (9认同)
  • @Eli:我同意这一点,但在一个非常特殊的情况下,如果真的需要它,那么代码仍然有效. (6认同)

jal*_*alf 72

正如UncleBens和Naveen所表明的那样,两者都有充分的理由.哪一个"更好"取决于您想要的行为:您是否希望保证恒定时间行为,或者您是否希望它在必要时回退到线性时间?

it - vec.begin()需要持续时间,但是operator -只在随机访问迭代器上定义,因此代码将不会使用列表迭代器进行编译.

std::distance(vec.begin(), it) 适用于所有迭代器类型,但如果在随机访问迭代器上使用,则只能是常量操作.

两者都不"更好".使用满足您需求的那个.

  • @ScaryAardvark:你的意思是期望它是O(1)吗? (6认同)

Eli*_*sky 11

我喜欢这个:it - vec.begin()因为对我来说它清楚地说"距离开始的距离".对于迭代器,我们习惯于在算术方面进行思考,因此-符号是这里最清晰的指标.

  • 使用减法来查找距离比使用,更确切地说是"距离"这个词更清楚? (17认同)
  • @Travis,对我而言.这是品味和习惯的问题.我们说`it ++`而不是'std :: increment(it)`,不是吗?那还不算不那么明确吗? (4认同)
  • `++`运算符被定义为STL序列的一部分,就像我们如何递增迭代器一样.`std :: distance`计算第一个和最后一个元素之间的元素数.`-`运算符的工作原理仅仅是巧合. (3认同)
  • @MSalters:然而,我们使用++ :-) (3认同)

AnT*_*AnT 8

如果您已经限制/硬编码的算法,使用std::vector::iteratorstd::vector::iterator而已,它并没有真正不管你最终会采用哪种方法.您的算法已经具体化,超出选择其中一个可以产生任何差异的点.他们都做了完全相同的事情.这只是个人喜好的问题.我个人会使用显式减法.

另一方面,如果你想在算法中保持更高的通用性,即允许将来某天可能应用于其他迭代器类型的可能性,那么最好的方法取决于你的意图.这取决于您希望在此处使用的迭代器类型方面的限制程度.

  • 如果使用显式减法,则算法将限制为一个相当窄的迭代器类:随机访问迭代器.(这是你现在得到的std::vector)

  • 如果使用distance,您的算法将支持更广泛的迭代器类:输入迭代器.

当然,distance非随机访问迭代器的计算通常是低效操作(同样,对于随机访问迭代,它与减法一样有效).您可以自行决定算法是否对非随机访问迭代器有意义.由此产生的效率损失对于使算法完全无用是毁灭性的,然后你应该更好地坚持减法,从而禁止低效使用并迫使用户为其他迭代器类型寻找替代解决方案.如果非随机访问迭代器的效率仍处于可用范围内,那么您应该使用distance并记录该算法在随机访问迭代器中更好地工作的事实.


Sté*_*ane 5

根据http://www.cplusplus.com/reference/std/iterator/distance/,由于vec.begin()随机访问迭代器,因此距离方法使用-运算符。

所以答案是,从性能的角度来看,它是相同的,但distance()如果有人必须阅读和理解你的代码,也许使用更容易理解。