【问题标题】:Peeking the next element in STL container查看 STL 容器中的下一个元素
【发布时间】:2011-04-10 01:49:56
【问题描述】:

是否可以在不更改迭代器的情况下查看迭代器当前指向的容器中的下一个元素?

例如在 std::set 中,

int myArray[]= {1,2,3,4};
set <int> mySet(myArray, myArray+4);
set <int>::iterator iter = mySet.begin();

//peek the next element in set without changing iterator.

mySet.erase(iter); //erase the element if next element is n+1

【问题讨论】:

  • 为什么不直接复制一份并推进那份呢?在继续之前,请确保检查任一迭代器是否等于 .end()
  • 我想我把迭代器和指针混淆了。当您将指针分配给指针时,如果一个指针发生变化,两者都会发生变化,但为什么在迭代器的情况下不会发生?
  • @GMan: Boost 有 priornext 函数来做到这一点(它按值获取迭代器,因此按照您的建议进行复制)。详情请见boost.org/libs/utility/utility.htm
  • @xor:如果您有两个指针,并更改其中一个,另一个保持不变。不过请理解,我们谈论的是更改指针,而不是指针的所指对象。
  • @xor:你应该小心你描述事物的方式。严格来说,你说的都是错误的。 j 不会随 i 改变,它是一个不同的变量,其值与 i 的值无关。 j 指向 的内容发生了变化,i 也发生了变化。

标签: c++ stl iterator


【解决方案1】:

C++0x 添加了一个方便的实用函数std::next,它复制一个迭代器,推进它,然后返回高级迭代器。您可以轻松编写自己的 std::next 实现:

#include <iterator>

template <typename ForwardIt>
ForwardIt next(ForwardIt it, 
               typename std::iterator_traits<ForwardIt>::difference_type n = 1)
{
    std::advance(it, n);
    return it;
}

您可以像这样在示例中使用它:

if (iter != mySet.end() && next(iter) != mySet.end() && *next(iter) == *iter + 1)
    mySet.erase(iter);

【讨论】:

  • +1,不知道被采纳了。顺便说一句,++ move( iter ) 做同样的事情。
  • 我不是模板方面的专家,所以我花了一段时间才完全理解这一小段代码,并且几乎采用了@AndrewShephard 提供的更简单的解决方案。但在最后一刻注意到这个解决方案在功能上与位于/usr/include/c++/${gcc_version}/bits/stl_iterator_base_funcs.h 的 gcc 的 4.4.x 和 4.9.x 源代码功能相同(但不完全相同),所以我改用了这个解决方案。
  • 我看到en.cppreference.com/w/cpp/iterator/next 说它是从 c++11 开始的,这是正确的吗?
  • @zhangxaochen:C++0x 是 C++11 的暂定名称(在计划推迟之前,他们认为它会在 2010 年之前发布); C++0x 和 C++11 真的是同一个意思。
【解决方案2】:

一般不使用迭代器。不保证迭代器能够非破坏性地运行。经典的例子是一个实际代表底层输入流的输入迭代器。

不过,有些东西适用于这种迭代器。 Forward Iterator 不会通过在集合中前进的行为使之前的副本失效。大多数迭代器(包括用于 STL 集合的迭代器)至少是前向迭代器,如果不是功能更强大的版本——只有输入迭代器或输出迭代器受到更多限制。所以你可以简单地复制你的迭代器,增加副本并检查那个,然后回到你原来的迭代器。

所以你的偷看代码:

set <int>::iterator dupe = iter;
++dupe;
// (do stuff with dupe)

【讨论】:

    【解决方案3】:

    这不适用于std::set,因为它的性质不允许使用 [] 运算符,但对于允许使用的容器,您可以这样做:

    std::vector<int> v;
    v.push_back(3);
    v.push_back(4);
    std::vector<int>::iterator it = v.begin(); 
    std::cout << v[it - v.begin() + 1];
    

    但是如果it 指向容器中的最后一个元素,这可能会很危险;但同样适用于上述解决方案。例如。您必须在这两种情况下进行检查。

    【讨论】:

    • 既然已经有了迭代器,为什么还要使用operator[]?如果您想要 it 当前指向的内容,请使用 *it。如果您想要it 之后的迭代器指向的内容,请使用*(it + 1)std::vector&lt;int&gt;::iterator it2(it); std::advance(it2, 1); *it2; 之类的东西。顺便提一下,提供的代码仅适用于std::vector(例如,它不适用于std::deque )。
    【解决方案4】:

    对于序列容器(向量、双端队列和列表),您可以调用 front,这会让您一目了然(更多信息请参见 link 的下部)。

    【讨论】:

      【解决方案5】:

      您可以随时制作迭代器的副本并推进副本:

      set <int>::iterator iter = mySet.begin();
      set <int>::iterator iterCopy = iter;
      iterCopy++;
      if (*iterCopy == something)
        mySet.erase(iter);
      

      但请注意,删除 iter 后,iterCopy 可能不再有效。

      【讨论】:

      • “一旦你删除 iter,iterCopy 可能不再有效”。它将适用于一组,但并非适用于所有 STL 容器。当我想要一个好的参考时,我会在谷歌上搜索“SGI STL set”或其他任何内容......涵盖了所有这些。
      • 但是请注意,SGI 是 STL 的重要资源。然而,就 C++ 标准库而言,它并不完全准确。 STL 包含几个非标准容器/算法,并且作为标准 C++ 一部分的容器在某些细节上不匹配,尤其是在分配器方面。
      【解决方案6】:
      set <int>::iterator iter2 = iter;
      ++iter2;
      int peekedValue = *iter2;
      

      【讨论】:

        猜你喜欢
        • 2010-12-15
        • 2011-09-11
        • 1970-01-01
        • 2021-11-15
        • 1970-01-01
        • 2011-07-08
        • 2011-09-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多