【问题标题】:iterate an STL container not from the .begin()ing and wrap around迭代一个不是从 .begin() 开始的 STL 容器并环绕
【发布时间】:2011-12-16 10:38:32
【问题描述】:

我有一个 std::vector,为了简单起见,我们说整数。

std::vector<int> ivec;
ivec.push_back(1);
ivec.push_back(2);
... //omitting some push back's 3 to 99
ivec.push_back(100);

标准的迭代方式是已知的

std::map<int>::iterator it;
for( it = ivec.begin(); it != ivec.end(); it++ ) 
  print();

该迭代将打印 1,2,3, ... 100。

我想遍历从预定义索引开始的所有向量元素,而不是从 it.begin() 开始。 我想打印

3,4,5,6 ... 99, 100, 1, 2

你能在这里分享你的想法吗?

分两步就可以了

for( it = ivec.begin()+index; it != ivec.end(); it++ ) and then (if index !=0)

for ( it = ivec.begin; it = it = ivec.begin() + (index-1); it++)

【问题讨论】:

  • 您可以编写一个迭代器包装器来显示所需的行为。但是,除非您需要多次,否则它可能是矫枉过正。
  • 你的最后一个例子是错误的。在第二个循环中,条件应该是 it != ivec.begin()+index(当然,这仅适用于随机迭代器)并且您不需要 if index != 0,循环条件会为您完成。

标签: c++ vector iterator std


【解决方案1】:

您可以:

  • 开发一个迭代器类,它包装 vector::iterator 并公开您喜欢的行为(特别是:++ 检查 end() 并将其替换为 begin() 并调整其他“边界值”)

  • 从 3 开始填充向量并在 100 处换行,以便标准迭代看起来如您所愿。

选择取决于向量的其他用途以及还需要什么迭代。

【讨论】:

  • @PaulManta:如果问题是“本地化”的,那是微不足道的。如果问题远非微不足道的话是“概括的”:如果您希望您的迭代与任何现有的 STL 算法一起工作......您必须定义一个迭代器。我认为您的反对票是基于偏见。
【解决方案2】:

我假设您已经有一个起始迭代器。如何获得这取决于您是使用可索引(向量)类型还是仅使用前向迭代器,还是键控类型。然后你可以做一个这样的循环:

type::iterator start_iter = /* something from collection, perhaps begin()+index */
type::iterator cur_iter = start_iter;
do
{
  //do something with cur_iter

  ++cur_iter;
  if( cur_iter == collection.end() )
    cur_iter = collection.begin();
} while( cur_iter != start_iter );

这是基本循环。

【讨论】:

  • 对不起,应该是.begin()
【解决方案3】:
bool wrapped = false;
for (auto it = vec.begin() + index; (it != vec.begin() + index) || !wrapped; ++it)
{
    if (it == vec.end())
    {
        it = vec.begin();
        wrapped = true;
    }
    std::cout << *it;
}

【讨论】:

  • 应该是|| 而不是&amp;&amp;
  • @ChristianRau 这会在一开始就打破循环,不是吗?
  • 不,因为一开始!wrappedtrue。包装 !wrapped 之后是 false 并且 OR 仅等效于迭代器条件。在您的情况下,它将在开始时终止,因为迭代器条件为假,因此是 AND。
  • 如果index0,这将不起作用,你会得到vec.begin() 两次。也许在内部 if 语句的顶部添加if (index == 0) break;
【解决方案4】:

我知道这是一个很老的问题,但没有人提到 std::rotate,我认为在某些情况下,它可能是适合这项工作的工具。

来自http://www.cplusplus.com/reference/algorithm/rotate/的修改示例:

#include <iostream>     // std::cout
#include <algorithm>    // std::rotate
#include <vector>       // std::vector

int main () {
  std::vector<int> myvector;

  // set some values:
  for (int i = 1; i < 10; ++i) myvector.push_back(i); // 1, 2, 3, ... 9

  std::rotate(myvector.begin(), myvector.begin() + 2, myvector.end());
                                                  // 3, 4, 5, 6 ... 9, 1, 2
  // print out content:
  std::cout << "myvector contains:";
  for (auto it = myvector.begin(); it != myvector.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

cpp.sh/3rdru

输出:

myvector contains: 3 4 5 6 7 8 9 1 2

注意,由于my_vector 是由std::rotate 函数修改的,如果您只想迭代一次向量,这既不是很有效也不是很有用。

然而,虽然这可能不是这个 SO 问题的最佳答案,但我希望它仍然可以为有类似问题的人提供一些价值。

【讨论】:

  • 您的代码修改了原始向量。在某些情况下可能并不理想。
  • 这是一个很好的观点,我会更新我的答案以使其更清楚。
【解决方案5】:

使用随机访问容器时的解决方案非常简单,请参见下面的代码。

std::vector<int> v ({1,3,4,5,6,7,8,9,10}); /* c++11 */

...

for (int i = 2; i < (10+2); ++i)
  std::cout << v[i % 10] << " ";

使用仅具有双向/前向迭代器的容器时的方法:

  std::list<int> l ({1,3,4,5,6,7,8,9,10}); /* c++11 */

  Iter start = l.begin ();
  std::advance (start, 4);

  ...

  Iter it = start;

  do {
    std::cerr << *it << std::endl;

  } while (
    (it = ++it == l.end () ? l.begin () : it) != start
  );

【讨论】:

    【解决方案6】:

    有无数种方法可以做到这一点,而且可能所有方法(或多或少)都是等效的,所以最终这取决于个人喜好,也可能取决于编码风格约定。我可能会这样做:

    std::cout << v[idx] << "\n";
    for( auto it = v.begin() + idx + 1; it != v.begin()+idx; ++it )
    {
        if( it == v.end() ) it = v.begin();
        std::cout << *it << "\n";
    }
    

    【讨论】:

    • @cateo:我总是用它来存储迭代器,因为它使代码看起来不那么复杂,而且在许多情况下,我可以更轻松地编写通用代码,因为我不必访问嵌套的 typedef。跨度>
    • OP 可能不知道,因为它是 C++11。
    猜你喜欢
    • 2020-01-15
    • 2013-10-25
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 2018-04-21
    相关资源
    最近更新 更多