有没有一种方法可以在不使用两个for循环的情况下遍历两个容器?

4
有没有一种方法可以在不使用两个for循环的情况下迭代两个容器(一个跟随另一个)?
我的意图是像这样做:
vector<int> a{ 1,2,3 };
vector<int> b{ 4,5,6 };

auto it = a.begin();
auto end = b.end();

for (; it != end; ++it)
{
    if (it == a.end())
    {
        it = b.begin();
    }
    // do something with *it
}

打印
1 2 3 4 5 6

(当然它不起作用。解释在这个答案中)
我不想写两个for循环并复制循环内的代码。有没有一种方法可以使用单个for循环迭代a和b?
我唯一能想到的就是将第二个容器复制/移动到第一个容器中,或者创建一个新的向量来合并a和b,然后迭代它。但我也不想这样做,因为这意味着昂贵的复制操作。

1
写两个循环怎么样(或者使用标准算法,比如 std::for_each),并将重复的行为实现在一个单独的函数中,该函数将被传递给该算法? - Fureeish
@Fureeish 是的,那是可能的。 - Mathai
有人可以解释一下为什么这个问题被踩了吗?我进行过研究,这不是一个作业问题。 - Mathai
那么我会选择这种方法。强制逻辑被包含在一个循环中会引入许多不必要的检查和难以阅读的代码噪音。尽量保持简单,特别是如果行为和性能在简单的方法方面相同或更好。 - Fureeish
9个回答

8
使用range-v3,它是C++17或更早版本中与范围相关的首选工具。
for (int i : view::concat(a, b)) {
    std::cout << i << ' ';
}

3
使用boost range的另一种方法来实现它。
#include <vector>
#include <iostream>

#include <boost/range.hpp>
#include <boost/range/join.hpp>

int main()
{
  std::vector<int> a{ 1,2,3 };
  std::vector<int> b{ 4,5,6 };

  for(auto& x : boost::join(a, b)) {
      std::cout << x << " ";
  }
  std::cout << std::endl;
}

2
Boost Range”和标准库算法是更好的设计方案,因此应该优先考虑使用。
然而,为了完整起见,如果您真的想要应用您设计背后的思想,可以按照以下方式编写代码:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {4, 5, 6};

for (auto it = v1.begin(); it != v2.end();) {
  if (it == v1.end()) {
    it = v2.begin();
  } else {
  // {
    // use of *it
  // }
    ++it;
  }
}

点此查看演示


使用Visual Studio 2017时,我遇到了“vector iterators incompatible”的错误。我猜测这是因为我们将其与v2.end()进行比较,而v1实际上是v1迭代器。 - Mathai
如果v1和v2都为空,你需要进行一些额外的检查。 - AndyG
@AndyG 不是的。代码检查了所有情况。我认为这里没有任何未定义行为。 - BiagioF
@BiagioFesta:你是对的,抱歉。我误读了use of *itelse之外的部分。 - AndyG

1
你可以这样使用 boost::range::join
#include <boost/range/join.hpp>

...

std::vector<int> a{ 1,2,3 };
std::vector<int> b{ 4,5,6 };

for (auto i : boost::range::join(a, b))
{
    ...
}

1
发现了一种易于“传统”地完成此操作的方法。
for (int i = 0; i < 2; i++)
{
    auto it = (i == 0) ? a.begin() : b.begin();
    auto end = (i == 0) ? a.end() : b.end();
    for (; it != end; ++it)
    {
        // do something with *it
    }
}

但是这个使用了两个for循环? - AndyG
@AndyG 抱歉如果我让大家感到困惑,我的意思是“不使用两个循环,一个跟着另一个”。不想重复代码“// do something with *it”。 - Mathai

0

甚至没有一个 for() 循环需要打印这些容器,如果你使用如下的 std::copy

#include <iostream>

#include <vector>

#include <iterator>
#include <algorithm>

int main(int , char *[])
{
    std::vector< int> a{ 1, 2, 3};
    std::vector< int> b{ 4, 5, 6};

    std::copy( a.begin(), a.end(), std::ostream_iterator< int>( std::cout, " "));
    std::copy( b.begin(), b.end(), std::ostream_iterator< int>( std::cout, " "));

    std::cout<< std::endl;

    return 0;
}

输出: 1 2 3 4 5 6

使用 STL 库是最佳选择,不需要编写任何代码来打印容器。

根据您的问题我不想写两个 for 循环并且在循环内重复相同的代码。有没有一种方法可以使用单个 for 循环迭代遍历 a 和 b?

避免重复代码的方法是编写可从多个地方使用的函数,例如如果您不想使用 std::copy 并且想要编写自己的代码来打印这些容器(不建议这样做),那么您可以编写以下函数:

template< typename ForwardIterator>
void print( ForwardIterator begin, ForwardIterator end, const std::string& separator)
{
    while( begin != end)
    {
        std::cout<< *begin<< separator;
        ++begin;
    }
}

然后调用print函数,

print( a.begin(), a.end(), std::string( " "));
print( b.begin(), b.end(), std::string( " "));

0
如果你想自己编写代码,以下内容可能会有所帮助:
template<class ForwardItr>
struct range {
    ForwardItr beg;
    ForwardItr end;
};

template<class ForwardItr, class F>
void concat_ranges(range<ForwardItr> r1, range<ForwardItr> r2, F f) {
    auto run = [&f](range<ForwardItr> r) {
        for(auto itr = r.beg; itr != r.end; ++itr){
            f(*itr);
        }
    };
    run(r1);
    run(r2);
};

例子:https://gcc.godbolt.org/z/8tPArY

(涉及编程相关内容)


-1

好的...你的错误是使用了双等号,而你需要使用单等号。

我的意思是,不是

if (it == a.end())
{
    it == b.begin();
} //   ^^ Wrong!

但是

if (it == a.end())
{
    it = b.begin();
} //   ^ correct

但我认为这不是一个好主意:我们能确定 a.end() != b.end() 吗?

你的代码取决于此。


-1
为了获得最优化的代码,最好避免在每个循环中进行不必要的测试。由于最有效的代码包括执行两个循环,因此可以通过改变参与循环的变量(迭代器和哨兵)的整个状态来接近这一点(我想这就是实现连接范围的方式...这就是我所做的)。
vector<int> a{ 1,2,3 };
vector<int> b{ 4,5,6 };

auto it = a.begin();
auto end = a.end();

for (;[&](){
         if (it==end){
            if (end==a.end()) {
              it=b.begin();
              end=b.end();
              return true;
              }
            else return false;
            }
          return true;
          }();
    ++it)
{
   //loop content
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接