有没有一种方法可以遍历两个容器而不使用两个for循环

时间:2019-01-23 21:48:42

标签: c++ c++11 c++14 c++17

有没有一种方法可以遍历两个容器(一个接着另一个),而无需使用两个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

(当然不起作用。解释在此answer中)

我不想编写两个for循环并在循环内复制代码。 是否可以通过单个for循环在a之后b上进行迭代?

我唯一想到的是将第二个容器复制/移动到第一个容器,或者创建一个结合了ab的新向量,然后对其进行迭代。我也不想这样做,因为这将意味着昂贵的复制操作。

9 个答案:

答案 0 :(得分:8)

使用range-v3处理C ++ 17或更早版本中与范围相关的所有事情:

for (int i : view::concat(a, b)) {
    std::cout << i << ' ';
}

答案 1 :(得分: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;
  }
}

Live Demo Here

答案 2 :(得分:2)

使用增强范围的另一种方法

#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;
}

答案 3 :(得分: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))
{
    ...
}

答案 4 :(得分: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
    }
}

答案 5 :(得分:1)

如果您想编写自己的书,则可以使用以下帮助:

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

答案 6 :(得分:0)

好吧...您的错误是一个双重等于,您需要一个单一相等。

我的意思不是

YelpModel

但是

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

但是我认为这不是一个好主意:我们确定if (it == a.end()) { it = b.begin(); } // ^ correct 吗?

您的代码取决于此。

答案 7 :(得分: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( " "));

答案 8 :(得分:-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
}