如何在类中实现标准迭代器

新手上路,请多包涵

我有通常使用标准容器作为基础字段的类。例如,我有一堂课

template <typename T>
class Vec_3D
{
public:
    /* ... */
    std::array<T, 3> vec;
    /* ... */
};

它只有一个变量 vec 其余的只是我在使用向量时需要的函数。我希望能够使用基于范围的 for 循环,例如

Vec_3D<double> vec;
for (double val : vec) {/*...*/}

这显然应该迭代 std::array<double, 3>

如何在我的类中实现迭代器,而这些迭代器又应该调用 std::array<T, 3> 的迭代器?

我从 这个问题 开始,并尝试在我的类中定义迭代器为

typedef std::iterator<std::random_access_iterator_tag, T, ptrdiff_t, T*, T&> iterator;
typedef std::iterator<std::random_access_iterator_tag, const T, ptrdiff_t, const T*, const T&> const_iterator;

inline iterator begin() noexcept { return vec.begin(); }
inline const_iterator cbegin() const noexcept { return vec.cbegin(); }
inline iterator end() noexcept { return vec.end(); }
inline const_iterator cend() const noexcept { return vec.end(); }

但出现编译错误

error: no match for ‘operator!=’ (operand types are ‘Vec_3D<double>::iterator {aka std::iterator<std::random_access_iterator_tag, double, long int, double*, double&>}’ and ‘Vec_3D<double>::iterator {aka std::iterator<std::random_access_iterator_tag, double, long int, double*, double&>}’)

operator++, operator*

原文由 Michal 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 389
2 个回答

A range-based for loop only requires that your class have begin() and end() methods (or overloads of std::begin() and std::end() ) that return迭代器。它不关心那些迭代器来自哪里。因此,最简单的解决方案是只使用数组自己的迭代器,而不是尝试定义自己的迭代器,例如:

 template <typename T>
class Vec_3D
{
public:
    typedef typename std::array<T, 3> array_type;
    typedef typename array_type::iterator iterator;
    typedef typename array_type::const_iterator const_iterator;
    // or:
    // using array_type = std::array<T, 3>;
    // using iterator = array_type::iterator;
    // using const_iterator = array_type::const_iterator;
    ...

    inline iterator begin() noexcept { return vec.begin(); }
    inline const_iterator cbegin() const noexcept { return vec.cbegin(); }
    inline iterator end() noexcept { return vec.end(); }
    inline const_iterator cend() const noexcept { return vec.cend(); }
    ...

private:
    array_type vec;
};

原文由 Remy Lebeau 发布,翻译遵循 CC BY-SA 4.0 许可协议

std::iterator 是(曾经)一个帮助器类型,用于定义典型迭代器所需的 typedef s。类中的这些 typedef 反过来使 std::iterator_traits 与您的迭代器一起工作。

但是,它实际上并没有为您实现所需的操作。

它已被弃用,因为标准委员会不喜欢指定标准迭代器必须具有那些 typedef,并且编写 typedef 并不比弄清楚要传递给 std::iterator 模板的参数要复杂得多。

这里最简单的做法就是窃取底层容器的迭代器。这会使您的抽象泄漏,但它既高效又容易。

 template <typename T>
struct Vec_3D {
  using container=std::array<T, 3>;
  using iterator=typename container::iterator;
  using const_iterator=typename container::const_iterator;

  iterator begin() { return vec.begin(); }
  iterator end() { return vec.end(); }
  const_iterator begin() const { return vec.begin(); }
  const_iterator end() const { return vec.end(); }
private:
  /* ... */
  container vec;
  /* ... */
};

如果你不想暴露你的底层容器类型,如果你愿意保证你的底层容器是一个连续的缓冲区,你可以这样做:

 template <typename T>
struct Vec_3D {
  using iterator=T*;
  using const_iterator=T const*;

  iterator begin() { return vec.data(); }
  iterator end() { return vec.data()+vec.size(); }
  const_iterator begin() const { return vec.data(); }
  const_iterator end() const { return vec.data()+vec.size(); }
private:
  /* ... */
  std::array<T,3> vec;
  /* ... */
};

因为指针是有效的迭代器。

如果你发现你写这个“我是一个修改过的容器”样板太多了,你可以自动化它:

 template<class Container>
struct container_wrapper {
  using container=Container;

  using iterator=typename container::iterator;
  using const_iterator=typename container::const_iterator;

  iterator begin() { return m_data.begin(); }
  iterator end() { return m_data.end(); }
  const_iterator begin() const { return m_data.begin(); }
  const_iterator end() const { return m_data.end(); }
protected:
  Container m_data;
};

接着

template <typename T>
class Vec_3D:private container_wrapper<std::array<T,3>> {
  // ...
};

但即使这样也可能有点多,为什么不只是:

 template <typename T>
class Vec_3D:public std::array<T,3> {
  // ...
};

确实,通过指向 base 的指针删除 Vec_3D 是未定义的行为,但谁会删除指向标准容器的指针?

如果这让您担心:

 template <typename T>
class Vec_3D: private std::array<T,3> {
  using container = std::array<T,3>;
  using container::begin();
  using container::end();
  // ...
};

允许您私下继承,然后将某些操作带回范围。

原文由 Yakk - Adam Nevraumont 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题