用于多维向量的迭代器,用作一维?

时间:2016-06-17 00:19:44

标签: c++ c++11 vector iterator std

我有一个看起来像这样的矢量:

std::vector<std::vector<MyClass>> myVector;

我想通过迭代器访问它的元素,好像它是一维的向量:

for (auto& x : myVector)
{
     foo(x); // x is an object of type MyClass
}

(即,存在多个维度的事实对于通过myVector循环的人来说是透明的)

我知道应该如何做,有一个自定义迭代器实现,可以保存当前索引,以便当其中一个向量没有更多元素时,它会重置其中一个索引并递增另一个索引以便它可以启动迭代下一个向量,依此类推。但我一直试图编写这个想法,但似乎无法实现这一点。有谁知道我怎么可能实现这个目标?或者甚至更好,如果有任何类似实现的开源项目?

感谢。

4 个答案:

答案 0 :(得分:1)

完全有可能定义自己的迭代器来隐藏迭代矢量向量的所有细节,我写了一些代码给你的想法,请注意它应该需要更多的检查,但它基本上有效,给你一个想法。

您只需要编写所需的操作,使其在其他代码中工作,如不透明的迭代器。

template <typename T>
struct vector2d
{
public:
  class iterator
  {
  public:
    using vector_type = std::vector<std::vector<T>>;
    using first_level_iterator = typename std::vector<std::vector<T>>::iterator;
    using second_level_iterator = typename std::vector<T>::iterator;

  private:
    vector_type& data;
    first_level_iterator fit;
    second_level_iterator sit;

  public:
    iterator(vector_type& data, bool begin) : data(data)
    {
      if (begin)
      {
        fit = data.begin();
        sit = fit->begin();
      }
      else
      {
        fit = data.end();
      }
    }

    inline bool operator!=(const iterator& other) const { return fit != other.fit || (fit != data.end() && sit != other.sit); }
    inline const iterator& operator++() {
      // don't go past end
      if (fit == data.end())
        return *this;

      // increment inner iterator
        ++sit;

      // if we reached the end of inner vector
      if (sit == fit->end())
      {
        // go to next vector
        ++fit;

        // if we reached end then don't reset sit since it would be UB
        if (fit != data.end())
        sit = fit->begin();
      }

      return *this;
    }

    T& operator*() const { return *sit; }
  };

public:
  std::vector<std::vector<T>> data;

  iterator begin() { return iterator(this->data, true); }
  iterator end() { return iterator(this->data, false); }
};

小测试:

int main() {
  vector2d<int> data;

  data.data.push_back(vector<int>());
  data.data.push_back(vector<int>());
  data.data.push_back(vector<int>());

  for (int i = 1; i < 5; ++i)
  {
    data.data[0].push_back(i);
    data.data[1].push_back(i*2);
    data.data[2].push_back(i*3);
  }

  for (auto i : data)
  {
    cout << i << endl;
  }

  return 0;
}

行为相当简单,但你必须确保它对所有边缘情况始终保持一致。

答案 1 :(得分:1)

非常小的范围类型:

template<class Outer, class Inner>
struct stacked_range_t {
  range_t<Outer> outer;
  stacked_range_t()=default;
  stacked_range_t( range_t<Outer> o ):outer(std::move(o)) {}
  struct iterator {
  private:
    range_t<Outer> outer;
    range_t<Inner> inner;
  public:
    iterator(
      range_t<Outer> o,
      range_t<Inner> i
    ):outer(std::move(o)), inner(std::move(i)) {}
    iterator()=default;

    friend auto mytie(iterator const& it) {
      return std::tie( it.outer.begin(), it.inner.begin(), it.inner.end() );
    }
    friend bool operator==(iterator const& lhs, iterator const& rhs) {
      return mytie(lhs)==mytie(rhs);
    }
    friend bool operator!=(iterator const& lhs, iterator const& rhs) {
      return mytie(lhs)==mytie(rhs);
    }
    using difference_type = std::ptrdiff_t;
    using value_type = typename std::iterator_traits<Inner>::value_type;
    using pointer = typename std::iterator_traits<Inner>::pointer;
    using reference = typename std::iterator_traits<Inner>::reference;
    using iterator_category = std::input_iterator_tag;

    reference operator*() const {
      return *inner.begin();
    }
    pointer operator->() const {
      return inner.begin().operator->();
    }
    iterator& operator++() {
      using std::begin; using std::end;
      inner = inner.without_front();
      while (inner.empty())
      {
        outer = outer.without_front();
        if (!outer.empty())
          inner = range( begin(outer.front()), end(outer.front()) );
      }
      return *this;
    }
    iterator operator++(int) {
      auto it = *this;
      ++*this;
      return it;
    }
  };
  iterator end() const {
    return { range( outer.end(), outer.end() ), {} };
  }
  // a bit tricky:
  iterator begin() const {
    if (outer.empty()) return end();

    auto rout = outer;
    while( !rout.empty() ) {
      using std::begin; using std::end;
      auto rin = range( begin(rout.front()), end(rout.front()) );
      if (!rin.empty())
        return {std::move(rout), std::move(rin)};
      rout = rout.without_front();
    }
    return end();
  }
};

使用范围比使用迭代器更容易执行此任务。

template<class Range>
auto make_stacked_range(Range& r) {
  using std::begin; using std::end;
  using Outer = decltype(begin(r));
  using Inner = decltype(begin(*begin(r));
  return stacked_range_t<Outer, Inner>{
    {begin(r), end(r)}
  };
}

以及创建它的函数:

{
"Cat": {
    "items": [
        {
         "posa": "a",
         "posb": "b",
         "posc": "c",
         "posd": "d",
         "pose": "e",
         "posf": "f"
        }
   ]
  },
"Dog": {
   "items" : [
       {
        "posa": "1",
        "posb": "2",
        "pos": "3",
        "posd": "4",
        "pose": "5",
        "posf": "6""
       }
    ]
}
可能存在拼写错误。使用C ++ 1z功能可以解决过于恼人的decltype表达式和辅助特征类。

依赖于可以简单构造的迭代器,并且这些简单构造的迭代器是相等的。

答案 2 :(得分:1)

尝试使用模板递归,例如:

#include <stdio.h>
#include <vector>
template <typename V>
void f(V& v){
    for(auto& e : v){
        f(e);
    }
    printf("\n");
}

template <>
void f(int& v){
    printf("%d ",v);
}

int main(){
    std::vector<int> v1={1,2};
    f(v1);
    std::vector<std::vector<int> > v2={{3,4},{5,6,7}};
    f(v2);
    return 0;
};

答案 3 :(得分:1)

使用range/v3

for (auto& x : myVector | ranges::view::join)
{
     foo(x); // x is an object of type MyClass&
}

Demo