std :: algorithms友好的方式迭代`std :: vector <std :: vector <something>&gt;`</std :: vector <something>

时间:2015-01-23 15:46:47

标签: c++ algorithm vector stl

我当前项目中的一个常见模式是:

for(auto& row: matrix)
{
    for(auto& col: row)
    {
        //insert simple operation here:
        //return when condition is true
        //increase counter when condition is true
        //etc
    }
}

正如您所看到的那样,条件完全适合某些std::algorithm,但我真的不知道如何迭代这个结构。由于这个大多数简单的事情,比如计算条件为真的元素,有几行而不是一行。

您能否建议我一种定义数据结构等矩阵的方法,以便与std::algorithms一起使用时很友好?

1 个答案:

答案 0 :(得分:1)

boost有多种方法可以将多个范围合并到一个范围内。

如果你不能使用boost,你可以这样写:

template<class Iterator>
struct Range {
  Iterator b_, e_;
  Range( Iterator b, Iterator e ):b_(b), e_(e) {}
  Iterator begin() const { return b_; }
  Iterator end() const { return e_; }
  bool empty() const { return begin() == end(); }
  template<class C>
  static auto adl_begin(C&& c) {
    using std::begin;
    return begin( std::forward<C>(c) );
  }
  template<class C>
  static auto adl_end(C&& c) {
    using std::end;
    return end( std::forward<C>(c) );
  }
  template<class C>
  explicit Range(C&& c):
    Range(adl_begin(std::forward<C>(c)), adl_end(std::forward<C>(c)))
  {}
  Range():b_(), e_() {}; // Range of pointers should be null null just in case
  Range( Range const& ) = default;
  Range( Range && ) = default;
  ~Range() = default;
  Range& operator=(Range const&)=default;
  Range& operator=(Range &&)=default;

  friend bool operator==( Range const& lhs, Range const& rhs ) {
    return lhs.b_ == rhs.b_ && lhs.e_ == rhs.e_;
  }
  friend bool operator!=( Range const& lhs, Range const& rhs ) {
    return !( lhs == rhs );
  }
};

以上是一个非常简单的Range模板,它允许您将一对迭代器存储在一个可以迭代并被视为一个单元的包中。 boost也有其中一个,可能写得更好。

接下来,两个嵌套范围的迭代器:

template<class Outer, class Inner>
struct bi_iterator:std::iterator<
  std::forward_iterator_tag,
  typename std::iterator_traits<Inner>::value_type
  // in theory add more, but I won't bother
> {
  using value_type = typename std::iterator_traits<Inner>::value_type;
  Range<Outer> outer;
  Range<Inner> inner;
  explicit bi_iterator( Range<Outer> out ):outer(out), inner(advance())
  {}
  friend bool operator==( bi_iterator const& lhs, bi_iterator const& rhs ) {
    if (lhs.outer != rhs.outer) return false;
    if (lhs.outer.empty()) return true;
    return lhs.inner == rhs.inner;
  }
  friend bool operator!=( bi_iterator const& lhs, bi_iterator const& rhs ) {
    return !(lhs==rhs);
  }
private:
  Range<Inner> advance() {
    if (outer.empty()) return;
    inner = Range<Inner>( *outer.begin() );
    rectify();
    return inner;
  }
  void rectify() {
    if (!inner.empty()) return;
    if (outer.empty()) return;
    outer = {std::next(outer.begin()), outer.end()};
    inner = advance();
  }
public:
  value_type& operator*() { return *inner.begin(); }
  bi_iterator operator++(int) {
    bi_iterator retval = *this;
    ++(*this);
    return retval;
  }
  bi_iterator& operator++() {
    inner = { std::next(inner.begin()), inner.end() };
    rectify();
    return *this;
  };
};
template<class Outer, class Inner>
Range<bi_iterator<Outer,Inner>> bi_range_helper( Range<Outer> out ) {
  return { bi_iterator<Outer,Inner>(out), bi_iterator<Outer,Inner>({out.end(), out.end()}) };
}
template<class Container>
auto bi_range( Container&& c ) {
  using std::begin; using std::end;
  auto b = begin(c);
  auto e = end(c);
  using Outer = decltype(b);
  using Inner = typename std::decay<decltype(begin(*b))>::type;
  return bi_range_helper<Outer,Inner>( {b,e} );
}

然后:

auto bi = bi_range( matrix );
for( auto&& element: bi ) { /* stuff */ }

应迭代matrix的第二维和

using std::begin; using std::end;
auto b = begin(bi);
auto e = end(bi);

应该是一些<algorithms>兼容的前向迭代器到矩阵的第二维中的元素。

请注意,上面可能存在一些错误,我在没有任何测试的情况下编写它,甚至编译它。

(旁白:在ADL兼容的上下文中始终使用std::beginstd::end,因此using std::begin子句。

如果您完成上述工作,那么这是一个有趣的项目:make nary_iterator可构建递归bi_iterator类型以链接任意深度。对于高级问题,请在不链接bi_iterator的情况下执行此操作。

请注意,我的bi_iterator将在很多方面不如推文版本(我忘记了它的名称),因为我只是把它推出来了,boost经过审核。