为什么要使用迭代器而不是数组索引?


239

采取以下两行代码:

for (int i = 0; i < some_vector.size(); i++)
{
    //do stuff
}

还有这个:

for (some_iterator = some_vector.begin(); some_iterator != some_vector.end();
    some_iterator++)
{
    //do stuff
}

有人告诉我第二种方法是首选。为什么会这样呢?


72
首选的第二种方法是更改some_iterator++++some_iterator。后增量创建不必要的临时迭代器。
杰森

6
您还应该带入end()声明子句。
Lightness Races in Orbit

5
@Tomalak:使用低效率的C ++实现的人vector::end可能比担心它是否脱离循环还存在更糟糕的问题要担心。就个人而言,我更喜欢清晰度-但如果find在终止条件下致电给我,我会担心。
史蒂夫·杰索普

13
@Tomalak:该代码不是草率的(嗯,可能是后增量的),它简洁明了,就C ++迭代器所允许的简洁而言。为了过早的优化,添加更多变量会增加认知能力。太草率了。
史蒂夫·杰索普

7
@Tomalak:如果不是瓶颈,那还为时过早。您的第二点对我来说似乎很荒谬,因为正确的比较不是it != vec.end()和之间的比较it != end,而是(vector<T>::iterator it = vec.begin(); it != vec.end(); ++it)和之间的比较(vector<T>::iterator it = vec.begin(), end = vec.end(); it != end; ++it)。我不需要数字符。一定要比其他人更喜欢,但是其他人对您的首选项的不同并不是“草率”,它是更简单的代码的首选,它具有较少的变量,因此在阅读时较少考虑。
史蒂夫·杰索普

Answers:


210

仅当vector.size()是快速操作时,第一种形式才有效。例如,向量是正确的,但列表则不是。另外,您打算在循环体内做什么?如果您打算像这样访问元素

T elem = some_vector[i];

那么您就假设容器已operator[](std::size_t)定义。同样,对于矢量而言,这是正确的,但对于其他容器,则不是。

迭代器的使用使您更接近于容器独立性。您并没有假设size()容器具有迭代器功能,而是假设它具有随机访问功能或快速操作。

您可以使用标准算法进一步增强代码。根据您要实现的目标,您可以选择使用std::for_each()std::transform()依此类推。通过使用标准算法而不是显式循环,您可以避免重新发明轮子。您的代码可能更有效(如果选择了正确的算法),正确且可重用。


8
您还忘记了迭代器可以执行快速故障之类的操作,因此,如果对要访问的结构进行并发修改,您将了解它。您不能仅使用整数来做到这一点。
Marcin

4
这使我感到困惑:“例如,对于矢量而言,这是正确的,但对于列表而言,则不然。” 为什么?有大脑的人都会size_t跟踪成员变量size()
GManNickG

19
@GMan-在几乎所有实现中,对于列表来说,size()都非常快,矢量也是如此。该标准的下一版本将要求此为真。真正的问题是按位置退回的速度很慢。
Daniel Earwicker,2009年

8
@GMan:存储列表大小要求列表切片和拼接为O(n)而不是O(1)。

5
在C ++ 0x中,要求size()成员函数对所有支持它的容器具有恒定的时间复杂度,包括std::list
James McNellis

54

它是现代C ++灌输过程的一部分。迭代器是迭代大多数容器的唯一方法,因此即使将向量用于向量,也可以使自己陷入正确的思维定式。认真地讲,这是我这样做的唯一原因-我认为我从未用其他类型的容器替换过矢量。


哇,三个星期后,它仍然被低估了。我想这有点不客气。

我认为数组索引更具可读性。它与其他语言中使用的语法以及老式C数组中使用的语法相匹配。它也不太冗长。如果您的编译器是好的,效率应该是洗钱,几乎在任何情况下它都至关重要。

即使这样,我仍然发现自己经常将向量与迭代器结合使用。我相信迭代器是一个重要的概念,因此我会尽可能推广它。


1
C ++迭代器在概念上也很糟糕。对于向量,我只是被捕获了,因为结束指针实际上是end + 1(!)。对于流,迭代器模型只是超现实的-不存在的虚构令牌。对于链表也是如此。该范式仅对数组有意义,然后就没有意义。为什么我需要两个迭代器对象,而不仅仅是一个...
调节式,2016年

5
@aberglas它们根本没有坏,您只是不习惯它们,这就是为什么即使您不必使用它们我也主张使用它们的原因!半开范围是一个常见的概念,永远不能直接访问的标记与编程本身一样古老。
Mark Ransom

4
看一看流迭代器,并考虑==为了适应模式已经被做了什么,然后告诉我迭代器没有坏!或用于链接列表。即使对于数组,也必须在末尾指定一个末尾,这是一个破烂的C风格想法-指向永不永不的指针。它们应该像Java或C#或任何其他语言的迭代器一样,需要一个迭代器(而不是两个对象)和一个简单的最终测试。
调谐的'16

53

因为您没有将代码绑定到some_vector列表的特定实现。如果使用数组索引,则必须是某种形式的数组;如果使用迭代器,则可以在任何列表实现中使用该代码。


23
std :: list接口故意不提供operator [](size_t n),因为它将为O(n)。
MSalters

33

想象some_vector是用链表实现的。然后,在第i个位置请求一个项目需要执行i个操作来遍历节点列表。现在,一般而言,如果使用迭代器,它将尽最大努力提高效率(对于链表,它将维护指向当前节点的指针并在每次迭代中进行推进,只需要一个单次操作)。

因此,它提供了两件事:

  • 使用的抽象:您只想迭代一些元素,而不关心如何做
  • 性能

1
“它将维护一个指向当前节点的指针并提高它的效率(关于效率的好东西)”-是的,我不明白为什么人们在理解迭代器的概念时会遇到麻烦。从概念上讲,它们只是指针的超集。当您可以仅缓存指向某个元素的指针时,为什么要一遍又一遍地计算某个元素的偏移量?好吧,这也是迭代器所做的。
underscore_d

27

我将在这里成为恶魔的拥护者,而不推荐迭代器。这样做的主要原因是,从台式机应用程序开发到游戏开发,我从事的所有源代码都不需要使用迭代器。一直以来都不需要它们,其次,迭代器带来的隐藏假设,代码混乱和调试噩梦使它们成为一个不用于任何需要速度的应用程序的典范。

即使从维护的角度来看,它们也是一团糟。不是因为它们,而是因为幕后发生的所有混叠。我怎么知道您还没有实现与标准完全不同的虚拟矢量或数组列表。我是否知道运行时当前是哪种类型?您是否使运算符超载了,我没有时间检查所有源代码。我甚至不知道您使用的是哪个版本的STL?

迭代器遇到的下一个问题是抽象泄漏,尽管有许多网站与它们进行了详细讨论。

抱歉,我还没有看到迭代器的任何意义。如果他们从您那里抽象出列表或向量,那么实际上您应该已经知道要处理的向量或列表(如果您不这样做),那么将来您将需要准备一些很棒的调试会话。


23

如果要在向量上进行迭代时向向量添加/删除项目,则可能需要使用迭代器。

some_iterator = some_vector.begin(); 
while (some_iterator != some_vector.end())
{
    if (/* some condition */)
    {
        some_iterator = some_vector.erase(some_iterator);
        // some_iterator now positioned at the element after the deleted element
    }
    else
    {
        if (/* some other condition */)
        {
            some_iterator = some_vector.insert(some_iterator, some_new_value);
            // some_iterator now positioned at new element
        }
        ++some_iterator;
    }
}

如果使用索引,则必须在数组中上/下随机排列项以处理插入和删除。


3
如果您想在容器的中间插入元素,那么向量可能不是一个很好的容器选择。当然,我们又回到了为什么迭代器很酷的原因。切换到列表很简单。
威廉希尔

但是,如果您建议使用链表而不是,则std::list与相比,迭代所有元素的开销相当高。请参阅第43页:ecn.channel9.msdn.com/events/GoingNative12/GN12Cpp11Style.pdf以我的经验,即使搜索所有元素并删除任意位置的元素,我发现a 的速度也比a快。std::vectorstd::vectorstd::vectorstd::list
David Stone

索引是稳定的,因此我看不到插入和删除还需要什么额外的改组。
musiphil

...并有了一个链表-这就是这里要使用的-循环语句for (node = list->head; node != NULL; node = node->next)将比您的前两行代码(声明和循环头)短。所以我再说一遍-使用迭代器和不使用迭代器之间的简洁性没有太大的根本区别- for即使您使用了while:声明,迭代和检查终止,您仍然满足语句的三个部分。
工程师

16

关注点分离

将迭代代码与循环的“核心”关注点分开是非常好的。这几乎是一个设计决定。

实际上,通过索引进行迭代将您与容器的实现联系在一起。向容器询问开始和结束迭代器,可使循环代码与其他容器类型一起使用。

另外,通过这种std::for_each方式,您可以告诉集合该怎么做,而不是向其内部进行询问

0x标准将引入闭包,这将使此方法更易于使用-看看Ruby的表现力[1..6].each { |i| print i; }...

性能

但是,也许可以忽略的一个问题是,使用该for_each方法可以使迭代并行化- 英特尔线程模块可以将代码块分配到系统中的处理器数量上!

注意:algorithms特别是在发现库之后foreach,我花了两三个月的时间来编写可笑的小型“帮助”运算符结构,这会使您的开发人员发疯。在这段时间之后,我回到了务实的方法-小环体不再值得foreach了:)

关于迭代器的必读参考是“ Extended STL”一书。

GoF在Iterator模式的末尾有一个很小的段落,它讨论了这种迭代品牌。它称为“内部迭代器”。也在这里看看。


15

因为它更加面向对象。如果要遍历索引,则假定:

a)这些对象是有序的
b)可以通过索引获得这些对象
c)索引增量将命中每一项
d)该索引从零开始

使用迭代器,您会说“给我所有东西,以便我可以使用它”而无需知道底层实现是什么。(在Java中,有些集合无法通过索引访问)

同样,使用迭代器,无需担心超出数组范围。


2
我认为“面向对象”不是正确的术语。迭代器在设计中不是“面向对象的”。它们促进函数式编程而不是面向对象的编程,因为它们鼓励将算法与类分开。
wilhelmtell

同样,迭代器也无助于避免越界。标准算法可以,但仅迭代器不能。
威廉希尔

足够公平@wilhelmtell,我显然是从以Java为中心的角度考虑这一点的。
cynicalman's

1
而且我认为它确实促进了OO,因为它是将集合的操作与该集合的实现分离开来。对象的集合不必一定知道应该使用什么算法来处理它们。
cynicalman's

实际上,那里有STL的版本已经检查了迭代器,这意味着当您尝试对该迭代器执行某些操作时,它将引发某种越界异常。

15

关于迭代器的另一个好处是,它们更好地允许您表达(并强制执行)您的const-preference。此示例确保您不会在循环中间更改向量:


for(std::vector<Foo>::const_iterator pos=foos.begin(); pos != foos.end(); ++pos)
{
    // Foo & foo = *pos; // this won't compile
    const Foo & foo = *pos; // this will compile
}

这看起来是合理的,但是我仍然怀疑是否是拥有它的原因const_iterator。如果我在循环中更改了矢量,我这样做是有原因的,并且在99.9%的时间内,更改不是偶然的,而对于其余部分,这仅仅是一个错误,就像代码中的任何错误一样需要修复。因为在Java和许多其他语言中,根本没有const对象,但是使用这些语言的用户永远不会遇到不支持const的问题。
neevek '16

2
@neevek如果那不是拥有它的原因const_iterator,那么到底是什么原因呢?
underscore_d

@underscore_d,我也想知道。我不是这方面的专家,只是答案对我而言难以说服。
neevek

15

除了所有其他出色的答案外,... int对于您的向量来说可能还不够大。相反,如果要使用索引,请size_type对容器使用:

for (std::vector<Foo>::size_type i = 0; i < myvector.size(); ++i)
{
    Foo& this_foo = myvector[i];
    // Do stuff with this_foo
}

1
@Pat Notz,这是一个很好的观点。在将基于STL的Windows应用程序移植到x64的过程中,我不得不处理数百次有关将size_t分配给可能导致截断的int的警告。
bk1e

1
更何况事实,大小类型是无符号和INT签订,所以你必须非直观的,错误隐藏转换正在进行只是比较int imyvector.size()
阿德里安·麦卡锡

12

我可能要指出你也可以打电话

std::for_each(some_vector.begin(), some_vector.end(), &do_stuff);


7

STL迭代器大部分都在那儿,因此诸如sort之类的STL算法可以与容器无关。

如果只想遍历向量中的所有条目,请使用索引循环样式。

对于大多数人来说,它的键入次数更少并且更易于解析。如果C ++有一个简单的foreach循环而不用模板魔术过度,那就太好了。

for( size_t i = 0; i < some_vector.size(); ++i )
{
   T& rT = some_vector[i];
   // now do something with rT
}
'

5

我认为矢量对它没有太大影响。我更喜欢自己使用索引,因为我认为它更具可读性,并且您可以进行随机访问,例如向前跳6个项目或在需要时向后跳。

我也喜欢像这样在循环中引用该项目,因此该地方周围没有​​很多方括号:

for(size_t i = 0; i < myvector.size(); i++)
{
    MyClass &item = myvector[i];

    // Do stuff to "item".
}

如果您认为将来将来可能需要用列表替换向量,并且使用STL怪胎看起来更时尚,则使用迭代器可能会很好,但是我无法想到任何其他原因。


大多数算法对容器的每个元素依次执行一次。当然,有些例外情况是您希望以特定的顺序或方式遍历一个集合,但是在这种情况下,我会尽力编写一种与STL集成并与迭代器一起使用的算法。
威廉希尔

这将鼓励重用,并避免以后出现一次性错误。然后,我将像其他标准算法一样使用迭代器来调用该算法。
wilhelmtell

1
甚至不需要advance()。迭代器具有与索引相同的+ =和-=运算符(用于向量容器和类似向量的容器)。
MSalters

I prefer to use an index myself as I consider it to be more readable仅在某些情况下;在其他情况下,索引很快变得非常混乱。and you can do random access这根本不是索引的唯一功能:请参见en.cppreference.com/w/cpp/concept/RandomAccessIterator
underscore_d

3

第二种形式表示您正在做的事更准确。在您的示例中,您实际上并不关心i的值-您想要的只是迭代器中的下一个元素。


3

在了解了有关此答案的内容后,我意识到这有点过分简化了。此循环之间的区别:

for (some_iterator = some_vector.begin(); some_iterator != some_vector.end();
    some_iterator++)
{
    //do stuff
}

这个循环:

for (int i = 0; i < some_vector.size(); i++)
{
    //do stuff
}

相当少。实际上,以这种方式执行循环的语法似乎在我身上不断发展:

while (it != end){
    //do stuff
    ++it;
}

迭代器确实释放了一些相当强大的声明性功能,并且当与STL算法库结合使用时,您可以做一些很酷的事情,这些事情超出了数组索引管理的范围。


事实是,如果所有迭代器都像您的最后一个示例一样紧凑,那么开箱即用,我将没有什么问题。当然,这实际上等于for (Iter it = {0}; it != end; ++it) {...}-您只是省略了声明-因此简洁与您的第二个示例没有太大不同。不过,+ 1。
工程师

3

索引编制需要额外的mul操作。例如,对于vector<int> v,编译器将转换v[i]&v + sizeof(int) * i


在大多数情况下,相对于迭代器而言,这可能不是一个明显的缺点,但这是一件好事。
nobar 2010年

3
对于隔离的单个元素访问,可能是。但是,如果我们谈论的是循环(就像OP一样),那么我很确定这个答案是基于虚构的非优化编译器。任何体面的人都有足够的机会和可能性来缓存该对象,sizeof并且每次迭代仅添加一次,而不是每次都进行整个偏移量计算。
underscore_d


2

尚未有人提到索引的一个优点是,当您将索引附加到一个连续容器时,它们不会变得无效 std::vector,因此您可以在迭代期间将项目添加到容器中。

迭代器也可以这样做,但是您必须调用reserve(),因此需要知道要追加多少个项目。


1

已经有几个好处。我还有一些其他评论:

  1. 假设我们在谈论C ++标准库,“向量”表示一个随机访问容器,该容器具有C数组的保证(随机访问,连续的内存布局等)。如果您说过“ some_container”,那么上面的许多答案都将更为准确(容器独立性等)。

  2. 为了消除对编译器优化的任何依赖,可以将some_vector.size()从索引代码中移出循环,如下所示:

    const size_t numElems = some_vector.size();
    对于(size_t i = 0; i 
  3. 始终在增量前进行迭代,并将增量后作为特殊情况。

for(some_iterator = some_vector.begin(); some_iterator!= some_vector.end(); ++ some_iterator){//做某事}

因此,假设和可索引的std::vector<>容器一样,没有充分的理由优先选择一个容器,而是依次遍历该容器。如果必须经常引用旧的或较新的元素索引,那么索引版本会更合适。

通常,首选使用迭代器,因为算法会使用它们,并且可以通过更改迭代器的类型来控制(并隐式记录)行为。可以使用数组位置代替迭代器,但是语法上的差异会突出。


1

我不使用迭代器的原因与我不喜欢foreach语句的原因相同。当有多个内部循环时,很难在不记住所有局部值和迭代器名称的情况下跟踪全局/成员变量。我发现有用的是在不同情况下使用两组索引:

for(int i=0;i<anims.size();i++)
  for(int j=0;j<bones.size();j++)
  {
     int animIndex = i;
     int boneIndex = j;


     // in relatively short code I use indices i and j
     ... animation_matrices[i][j] ...

     // in long and complicated code I use indices animIndex and boneIndex
     ... animation_matrices[animIndex][boneIndex] ...


  }

例如,我什至不想将诸如“ animation_matrices [i]”之类的缩写缩写为某些随机的“ anim_matrix”命名为迭代器,因为这样您就无法清楚地看到此值源自哪个数组。


在这种意义上,我看不到索引有什么更好的选择。你可以很容易地使用迭代器和随便挑一个约定他们的名字:itjtkt等,或者甚至只是继续使用ijk等如果你需要知道迭代器代表什么的,然后对我喜欢的东西for (auto anim = anims.begin(); ...) for (auto anim_bone = anim->bones.begin(); ...) anim_bone->wobble()会更多描述而不需要像这样不断索引animation_matrices[animIndex][boneIndex]
underscore_d

哇,我写这个意见的感觉就像很久以前了。如今,同时使用foreach和c ++迭代器并没有太多困难。我猜想多年使用错误代码会建立人的容忍度,因此更容易接受所有语法和约定……只要
可行

哈哈,的确,我真的没看过它几岁了!我上次没想到的其他地方是,如今我们还具有基于范围的for循环,这使得基于迭代器的方法更加简洁。
underscore_d

1
  • 如果您喜欢紧要关头/不信任其实现细节,请不要使用迭代器。
  • 如果在开发过程中定期将一种集合类型切换为另一种集合类型,请使用迭代器。
  • 如果您发现难以记住如何迭代不同种类的集合(也许您正在使用来自多个不同外部源的几种类型),请使用迭代器来统一遍历元素的方式。这适用于说用数组列表切换链接列表。

真的,这就是全部。这并不是说您平均可以通过任何一种方式来获得更多的简洁,并且如果简洁确实是您的目标,那么您总是可以依靠宏。


1

如果可以访问C ++ 11功能,则还可以使用基于范围的for循环对向量(或任何其他容器)进行迭代,如下所示:

for (auto &item : some_vector)
{
     //do stuff
}

此循环的好处是,您可以直接通过item变量访问向量的元素,而不必担心会弄乱索引或在取消引用迭代器时犯错误。此外,占位符使auto您不必重复容器元素的类型,这使您更接近于与容器无关的解决方案。

笔记:

  • 如果您需要循环中的element索引和operator[]容器的存在(并且对您而言足够快),那么最好采用第一种方法。
  • 基于范围的for循环不能用于向容器中添加元素或从容器中删除元素。如果您想这样做,那么最好坚持使用Brian Matthews给出的解决方案
  • 如果您不想更改容器中的元素,则应使用const如下关键字:for (auto const &item : some_vector) { ... }

0

比“告诉CPU做什么”(命令)甚至更好的是“告诉库想要什么”(功能)。

因此,代替使用循环,您应该学习stl中存在的算法。



0

我一直使用数组索引,因为我的许多应用程序都需要“显示缩略图”之类的东西。所以我写了这样的东西:

some_vector[0].left=0;
some_vector[0].top =0;<br>

for (int i = 1; i < some_vector.size(); i++)
{

    some_vector[i].left = some_vector[i-1].width +  some_vector[i-1].left;
    if(i % 6 ==0)
    {
        some_vector[i].top = some_vector[i].top.height + some_vector[i].top;
        some_vector[i].left = 0;
    }

}

0

两种实现都是正确的,但我希望使用“ for”循环。由于我们决定使用Vector而不是其他任何容器,因此使用索引将是最佳选择。与Vectors一起使用迭代器将失去将对象放在连续内存块中的巨大好处,这有助于简化对象的访问。


2
“将迭代器与Vectors一起使用将失去将对象放在连续的内存块中的巨大好处,这有助于简化其访问。” [需要引用]。为什么?您是否认为不能将迭代器的增量作为简单的加法实现?
underscore_d

0

我觉得这里没有任何答案可以解释为什么我喜欢迭代器作为索引而不是索引到容器的一般概念。请注意,我使用迭代器的大部分经验实际上并非来自C ++,而是来自诸如Python之类的高级编程语言。

迭代器接口对您的函数的使用者施加了更少的要求,从而使使用者可以使用它做更多的事情。

如果您只需要能够转发即可,则开发人员不仅限于使用可索引的容器-他们可以使用任何实现类的类operator++(T&)operator*(T)以及operator!=(const &T, const &T)

#include <iostream>
template <class InputIterator>
void printAll(InputIterator& begin, InputIterator& end)
{
    for (auto current = begin; current != end; ++current) {
        std::cout << *current << "\n";
    }
}

// elsewhere...

printAll(myVector.begin(), myVector.end());

您的算法可以在您需要的情况下工作-在向量上进行迭代-但对于您不一定期望的应用程序也很有用:

#include <random>

class RandomIterator
{
private:
    std::mt19937 random;
    std::uint_fast32_t current;
    std::uint_fast32_t floor;
    std::uint_fast32_t ceil;

public:
    RandomIterator(
        std::uint_fast32_t floor = 0,
        std::uint_fast32_t ceil = UINT_FAST32_MAX,
        std::uint_fast32_t seed = std::mt19937::default_seed
    ) :
        floor(floor),
        ceil(ceil)
    {
        random.seed(seed);
        ++(*this);
    }

    RandomIterator& operator++()
    {
        current = floor + (random() % (ceil - floor));
    }

    std::uint_fast32_t operator*() const
    {
        return current;
    }

    bool operator!=(const RandomIterator &that) const
    {
        return current != that.current;
    }
};

int main()
{
    // roll a 1d6 until we get a 6 and print the results
    RandomIterator firstRandom(1, 7, std::random_device()());
    RandomIterator secondRandom(6, 7);
    printAll(firstRandom, secondRandom);

    return 0;
}

试图实现一个类似于此迭代器的操作的方括号运算符会很麻烦,而迭代器的实现则相对简单。方括号运算符还会影响类的功能-您可以将其索引到任意点-可能难以实现或效率低下。

迭代器还适合进行装饰。人们可以编写迭代器,这些迭代器在其构造函数中使用迭代器并扩展其功能:

template<class InputIterator, typename T>
class FilterIterator
{
private:
    InputIterator internalIterator;

public:
    FilterIterator(const InputIterator &iterator):
        internalIterator(iterator)
    {
    }

    virtual bool condition(T) = 0;

    FilterIterator<InputIterator, T>& operator++()
    {
        do {
            ++(internalIterator);
        } while (!condition(*internalIterator));

        return *this;
    }

    T operator*()
    {
        // Needed for the first result
        if (!condition(*internalIterator))
            ++(*this);
        return *internalIterator;
    }

    virtual bool operator!=(const FilterIterator& that) const
    {
        return internalIterator != that.internalIterator;
    }
};

template <class InputIterator>
class EvenIterator : public FilterIterator<InputIterator, std::uint_fast32_t>
{
public:
    EvenIterator(const InputIterator &internalIterator) :
        FilterIterator<InputIterator, std::uint_fast32_t>(internalIterator)
    {
    }

    bool condition(std::uint_fast32_t n)
    {
        return !(n % 2);
    }
};


int main()
{
    // Rolls a d20 until a 20 is rolled and discards odd rolls
    EvenIterator<RandomIterator> firstRandom(RandomIterator(1, 21, std::random_device()()));
    EvenIterator<RandomIterator> secondRandom(RandomIterator(20, 21));
    printAll(firstRandom, secondRandom);

    return 0;
}

尽管这些玩具看起来很平凡,但不难想象使用迭代器和迭代器装饰器通过一个简单的界面即可完成强大的功能-例如,使用一个迭代器来装饰数据库结果的前向迭代器,该迭代器可从单个结果构造一个模型对象。这些模式可实现无限集的内存高效迭代,并且使用如我上面所写的过滤器,可能对结果进行延迟评估。

C ++模板的部分功能是迭代器接口,当将其应用于固定长度C数组之类时,它会退化为简单高效的指针算法,从而使其真正成为零成本的抽象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.