如何在类中实现标准迭代器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在类中实现标准迭代器相关的知识,希望对你有一定的参考价值。
我有一些类通常使用标准容器作为底层字段。例如,我有一个班级
template <typename T>
class Vec_3D
{
public:
/* ... */
std::array<T, 3> vec;
/* ... */
};
它只有一个变量vec
,其余的只是我在处理矢量时需要的函数。我希望能够使用基于范围的for循环,例如
Vec_3D<double> vec;
for (double val : vec) {/*...*/}
这显然应该在qazxsw poi上迭代。
如何在我的类中实现迭代器,它应该反过来调用std::array<double, 3>
的迭代器?
我开始使用std::array<T, 3>
并尝试在我的类中定义迭代器
this question
但得到了编译错误
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&>}’)
基于范围的for循环只需要您的类具有返回迭代器的operator++, operator*
和begin()
方法(或end()
和std::begin()
的重载)。它并不关心迭代器的来源。因此,最简单的解决方案是使用数组自己的迭代器而不是尝试定义自己的迭代器,例如:
std::end()
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;
};
是一种辅助类型,用于定义典型迭代器所需的std::iterator
s。这个类中的typedef反过来使typedef
与你的迭代器一起工作。
但是,它实际上并没有为您实现所需的操作。
它被弃用了,因为std委员会不喜欢指定标准迭代器必须具有那些typedef,并且编写typedef并不比确定传递给std::iterator_traits
模板的参数要大得多。
这里最简单的方法就是窃取你的底层容器的迭代器。这会使您的抽象泄漏,但它是高效和简单的。
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>> {
// ...
};
确实,通过指向base的指针删除template <typename T>
class Vec_3D:public std::array<T,3> {
// ...
};
是未定义的行为,但谁删除了指向标准容器的指针?
如果这让你担心:
Vec_3D
允许您私下继承,然后将某些操作带回范围。
std :: iterator只是一个基类,它基本上是一些特性的容器,但如果你想用它来实现你自己的迭代器类,你需要从它派生。
但是,您不需要使用它,有一个建议弃用它,您可以直接在您编写的迭代器中定义这些特征。以下问题包含有关该提议的信息以及有关实现迭代器类的帮助: - template <typename T>
class Vec_3D: private std::array<T,3> {
using container = std::array<T,3>;
using container::begin();
using container::end();
// ...
};
目前,您正在使用该基础定义容器的迭代器类型,而不是实际可以执行任何迭代的类,这就是失败的原因。
您将数组公开为公共成员。如果您很高兴公开您的vec_3d是使用数组实现的(无论您是否继续公开公开成员数组),那么您可以只使用数组的迭代器 - 从您的迭代器需要任何定制行为的问题中不清楚只是因为你的容器增加了一些功能。
以上是关于如何在类中实现标准迭代器的主要内容,如果未能解决你的问题,请参考以下文章
csharp 通用检查器类,它允许您在类文件中而不是在单独的自定义编辑器中实现OnScene / InspectorGUI。它也提供