【问题标题】:Iterating over more than one seq. container in C++11迭代多个序列。 C++11 中的容器
【发布时间】:2014-04-04 05:22:19
【问题描述】:

对于以下情况,我需要一条建议 - 我数小时都想不通: 如何遍历多个 seq 。以简单的方式具有相同大小的容器(这里:两个向量)?

int main() {
  int size = 3;
  std::vector<int> v1{ 1, 2, 3 }, v2{ 6, 4, 2 };

  // old-fashioned - ok
  for (int i = 0; i < size; i++) {
    std::cout << v1[i] << " " << v2[i] << std::endl;
  }

  // would like to do the same as above with auto range-for loop
  // something like this - which would be fine for ONE vector.
  // But this does not work. Do I need a hand-made iterator instead?
  for (const auto& i:v1,v2) {
    std::cout << i << " " << i << std::endl;
  }

  return EXIT_SUCCESS;
}

谢谢!

【问题讨论】:

标签: c++ c++11 iterator


【解决方案1】:

基于范围的for 循环旨在方便迭代一个范围,因为它是迄今为止最常见的情况。如果您需要迭代多个范围,这不是最常见的情况,您仍然可以使用传统方式:

for (auto i1 = begin(v1), i2 = begin(v2), e = end(v1); i1 != e; ++i1, ++i2)
{
  // processing
}

【讨论】:

  • +1 ... 并非所有内容都需要硬塞到一种特定的语言功能中。 C++ 的演变特别强调了可以很好地协同工作的有用特性。一个新功能不会淘汰另一个新功能,而是它们都在努力使语言变得更好。
  • Core 语言工作组实际上有一个积极的请求,要求支持使用基于范围的 for 循环同时迭代多个序列的语法,所以这根本不是牵强附会(但显然不是C++14),见cplusplus.github.io/EWG/ewg-active.html#43
  • 不应该是e1 = end(v1), e2 = end(v2); i1 != e1 &amp;&amp; i2 != e2吗?我意识到在这种情况下,这两个向量恰好具有相同的长度,但是应该编写 for 循环来依赖它吗?
  • @DavidConrad OP 在问题中声明“不止一个 seq . 相同大小的容器”,所以我相应地给出了答案。在一般情况下,有多种方法可以解决这个问题 - 将相同的长度作为前提条件,在最短时终止,提前测试,...
【解决方案2】:

Boost.Range 中有boost::combine() 允许写字

#include <iostream>
#include <iterator>
#include <vector>
#include <boost/range/combine.hpp>

int main()
{
    std::vector<int> v1{ 1, 2, 3 }, v2{ 6, 4, 2 };

    for (auto&& t : boost::combine(v1, v2))
            std::cout << t.get<0>() << " " << t.get<1>() << "\n";    
}

Live Example.

如果你不喜欢依赖这个,你可以用Boost.Iteratorzip_iteratorBoost.Rangeiterator_range和一点点C++自己拼出combine()功能14 种推导的返回类型:

template<class... Ranges>
auto combine(Ranges const&... ranges) 
// add -> decltype( boost::make_iterator_range(...) ) in C++11
{
    return boost::make_iterator_range(
        boost::make_zip_iterator(boost::make_tuple(begin(ranges)...)),
        boost::make_zip_iterator(boost::make_tuple(end(ranges)...))        
    );
}

Live Example.

解释boost::make_zip_iterator 在您的输入范围内创建了一个 boost::tuple 迭代器,并重载了您从常规迭代器中了解和喜爱的所有常用 operator++operator*。然后iterator_range 将其中两个zip_iterators 包装到一个带有begin()end() 函数的包中,允许C++11 range-for 循环使用它。它也可以推广到两个以上的输入范围。您可以使用 .get&lt;K&gt; 成员函数从元组中解压缩 K-th 元素。

【讨论】:

  • 啊,Boost...从 1999 年开始“简化”您的代码 :-)
  • 嗯,我看到了优点,zip 迭代器(或范围)是非常基本的东西,确实应该存在。我只是希望你能把它拼写成begin(v1, v2) 左右......
  • @KerrekSB:实际上,我希望我能说for (auto&amp;&amp; t: zip(v1, v2))zip 在大约...所有函数式语言中都很有名。当然,真正的问题是我们在 C++ 中没有元组解包(因为元组是库功能,而不是语言功能),所以可用性get&lt;0&gt;(t) 的计数有点下降。然而,zip 方法的设计存在一个重要问题:如何处理不同长度的序列?而且我不知道 Boost 如何处理这种情况(典型的选项是:在最短时停止,用给定元素填充,用 null 填充)。
  • @MatthieuM。请参阅boost::combine 的更新答案!
  • @TemplateRex:太棒了!我检查了长度不等的输入的行为,这是一个混合包:it computes the distance 以防止第二个序列比第一个序列短,但反过来it's not as nice
猜你喜欢
  • 2013-08-14
  • 2015-09-06
  • 2012-11-07
  • 2015-04-24
  • 2012-02-05
  • 1970-01-01
  • 2017-05-08
  • 2012-07-06
  • 1970-01-01
相关资源
最近更新 更多