我的类通常使用标准容器作为底层字段。例如,我有一个班级
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>
的迭代器?
我从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&>}’)
和operator++, operator*
答案 0 :(得分:9)
std::iterator
是一个帮助器类型,用于定义典型迭代器所需的typedef
。这些类中的typedef反过来使std::iterator_traits
与你的迭代器一起工作。
但是,它实际上并没有为您实现所需的操作。
它已被弃用,因为std委员会不喜欢指定标准迭代器必须具有那些typedef,并且编写typedef并不比确定传递给std::iterator
模板的参数要多得多。 / p>
这里最简单的方法就是窃取你的底层容器的迭代器。这会使您的抽象泄漏,但它既高效又简单。
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();
// ...
};
允许您私下继承,然后将某些操作带回范围。
答案 1 :(得分:7)
基于范围的for循环只要求您的类具有返回迭代器的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;
};
答案 2 :(得分:3)
std :: iterator只是一个基类,它基本上是一些特性的容器,但如果你想用它来实现你自己的迭代器类,你需要从它派生。
但是您不需要使用它,有一个建议弃用它,您可以直接在您编写的迭代器中定义这些特征。以下问题有关于该提议的信息以及有关实现迭代器类的帮助: - Preparation for std::iterator Being Deprecated
目前,您正在使用该基础定义容器的迭代器类型,而不是实际可以执行任何迭代的类,这就是失败的原因。
您将数组公开为公共成员。如果您很高兴公开您的vec_3d是使用数组实现的(无论您是否继续公开公开成员数组),那么您可以只使用数组的迭代器 - 从您的迭代器需要任何定制行为的问题中不清楚只是因为你的容器增加了一些功能。