以下代码显示了我目前拥有的内容。它是一个适配器
循环数据结构。主要功能显示了它的使用方式。这个
一切都很好但很快,但我真的想让迭代器结束
由circ
定义的结构。到目前为止所有方法都涉及到
一些计数方案(如果使用循环器计算范围,
构建一个计算增量和减量的迭代器)或布尔值
用于检查迭代器是否已移动以避免开始和结束的值
平等。
是否有一些通用的解决方案来适应循环结构 迭代器?还有哪些可行的解决方案?
我想尽可能保持迭代速度,但是 我愿意在这里妥协。我认为完全符合要求 迭代器超过一个小的速度惩罚。
#include <cstddef> // nullptr
#include <iostream>
#include <boost/noncopyable.hpp>
#include <boost/operators.hpp>
// circular structure that we want to iterate over
struct circ : private boost::noncopyable {
unsigned int i;
circ* next;
circ* prev;
};
// whacked up circulator, imagine some template cream here to make it
// generic, omitted to preserve sanity
struct circulator
: public boost::incrementable<circulator>
, public boost::decrementable<circulator>
, public boost::equality_comparable<circulator, circulator>
, public boost::dereferenceable<circulator, circ*>
{
circulator()
: c(nullptr) {}
circulator(circ& c) : c(&c) {}
bool operator==(const circulator& other) const {
return this->c == other.c;
}
circulator& operator++() { c = c->next; return *this; }
circulator& operator--() { c = c->prev; return *this; }
explicit operator bool() const { return c; }
circ& operator*() const { return *c; }
circ* c;
};
int main()
{
circ a, b, c, d;
a.next = &b; a.prev = &a; a.i = 0;
b.next = &c; b.prev = &a; b.i = 1;
c.next = &d; c.prev = &b; c.i = 2;
d.next = &a; d.prev = &c; d.i = 3;
circulator begin{a}, end{a};
if(begin)
do {
std::cout << begin->i << std::endl;
begin = ++begin;
} while(begin != end);
return 0;
}
如果需要,我可以添加一些我以前的方法,但它们是 相当冗长,会给问题增加不必要的膨胀。
编辑:如果生成的迭代器是双向的,那将是很好的。虽然我可以放弃这个要求。
答案 0 :(得分:2)
如果是我,我会operator++
注意到终端条件,并将c
设置为某个sentinal值:
circulator(circ& c) : c(&c), start(&c) {}
circulator& operator++() { c = c->next; if(c==start) c=nullptr; return *this; }
用法:
circulator begin{a}, end;
while(begin != end) {
begin++;
}
请注意,此用法将结束迭代器定义为持有nullptr,这意味着您无法执行此操作:
circulator end;
--end;
答案 1 :(得分:1)
通常,&#34;循环器&#34;表示线性结构的循环迭代适配器。你想要的实际上是一个反向适配器:它采用圆形结构,并呈现一个具有开头和结尾的线性迭代器 - 这些概念根本不适用于圆形迭代器或圆形结构。
因此,为了避免混淆,我将迭代器称为circ_iterator
。你的循环结构的真circulator
是微不足道的,不能关心任何目的或开头。
通过标记迭代器可以获得所需的功能:
获取start
类型的end
/ T
迭代器的惯用方法是通过begin
和end
在名称空间{{1}生命,或通过同名的成员函数。实例化T
将是非惯用的。相反,在circ_iterator end{a}
上重载begin
和end
。两者都返回指向参数的迭代器。 circ&
标记迭代器begin
,Default
标记迭代器end
。有关详细信息,请参阅this question。
只有end迭代器是特殊的,并且可以通过向迭代器添加一个小的三值标记来获得所有典型的迭代器语义。它的价值是:
End
; end
并且最近增加了; 从end
获取的迭代器将永久保留其end
标记。否则,迭代器以End
标记开头,并在递增时切换到Default
,并在递减时切换回Inc
。
请注意,Default
和begin
永远不会相同,因为圆形容器无法使零大小:end
项始终保持为至少一个数据项。当然,您可以使用null迭代器表示缺少circ
实例,该迭代器与任何其他null迭代器进行比较。
增量操作是特殊的,因为接近结束迭代器的唯一合法方法是递增。执行此操作时,必须执行以下操作:
因此,当指针相同时,迭代器是相同的,并且:
由于标记很小(2位宽),因此您可以假设或静态断言circ
类型与4个字节和特定于平台的{{1}对齐}&lt; - &gt; circ
转换是&#34; sane&#34;,并使用标记指针技巧将标记保留在指针的最低有效位中。我提供了使用标记指针技巧的版本和不使用标记指针技巧的版本。
最后,通过派生uintptr_t
来实现迭代器要容易得多。我将*circ
的实施作为练习留给读者。 It is well documented
代码在MSVC2012和LLVM 6上编译。
首先,让我们处理标记指针 - 这是一个非常基本的实现,但是我们会这样做。
boost::iterator_facade
const_circ_iterator
类可以有一些方便构造函数,使构造循环列表更容易,并避免你在问题中犯的错误(// https://github.com/KubaO/stackoverflown/tree/master/questions/circ-
iterator-9993713
#include <boost/iterator/iterator_facade.hpp>
#include <boost/noncopyable.hpp>
#include <boost/operators.hpp>
#include <limits>
#include <iostream>
#include <cassert>
#include <cstdint>
#include <algorithm>
template <typename T, bool merge_tag = false, typename tag_type = uint8_t> class tagged_ptr;
template <typename T, typename tag_type> class tagged_ptr<T, true, tag_type> {
uintptr_t ptr;
typedef std::numeric_limits<uintptr_t> lim;
inline static uintptr_t ptr_of(T* p) {
assert(tag_of(p) == 0);
return uintptr_t(p);
}
inline static uintptr_t tag_mask() { return 3; }
inline uintptr_t ptr_only() const { return ptr & (lim::max() - tag_mask()); }
inline static tag_type tag_of(T* p) { return ((tag_type)(uintptr_t)p) & tag_mask(); }
inline tag_type tag_only() const { return ptr & tag_mask(); }
public:
tagged_ptr(T* p, tag_type t) : ptr(ptr_of(p) | t) { assert(t <= tag_mask()); }
tagged_ptr(const tagged_ptr & other) : ptr(other.ptr) {}
operator T*() const { return reinterpret_cast<T*>(ptr_only()); }
T* operator->() const { return reinterpret_cast<T*>(ptr_only()); }
tagged_ptr & operator=(T* p) { ptr = tag_only() | ptr_of(p); return *this; }
tag_type tag() const { return tag_only(); }
void set_tag(tag_type tag) { assert(tag <= tag_mask()); ptr = tag | ptr_only(); }
};
template <typename T, typename tag_type> class tagged_ptr<T, false, tag_type> {
T* ptr;
tag_type m_tag;
public:
tagged_ptr(T* p, tag_type t) : ptr(p), m_tag(t) {}
tagged_ptr(const tagged_ptr & other) : ptr(other.ptr), m_tag(other.m_tag) {}
operator T*() const { return ptr; }
T* operator->() const { return ptr; }
tagged_ptr & operator=(T* p) { ptr = p; return *this; }
tag_type tag() const { return m_tag; }
void set_tag(tag_type tag) { m_tag = tag; }
};
错误)。
circ
circ_iterator是:
a.prev = &a
最后,一个简单的演示:
struct circ : private boost::noncopyable {
unsigned int i;
circ* next;
circ* prev;
explicit circ(int i) : i(i), next(nullptr), prev(nullptr) {}
circ(int i, circ& prev) : i(i), next(nullptr), prev(&prev) {
prev.next = this;
}
circ(int i, circ& prev, circ& next) : i(i), next(&next), prev(&prev) {
prev.next = this;
next.prev = this;
}
};
输出:
class circ_iterator;
circ_iterator end(circ& c);
class circ_iterator
: public boost::iterator_facade<
circ_iterator, circ, boost::bidirectional_traversal_tag
>
{
tagged_ptr<circ> c;
enum { Default, Inc, End };
friend class boost::iterator_core_access;
friend circ_iterator end(circ&);
struct end {};
circ_iterator(circ& c_, end) : c(&c_, End) {}
circ& dereference() const { return *c; }
void increment() {
c = c->next;
if (c.tag() != End) c.set_tag(Inc);
}
void decrement() {
c = c->prev;
if (c.tag() != End) c.set_tag(Default);
}
bool equal(const circ_iterator & other) const {
return this->c == other.c &&
(other.c.tag() != End || this->c.tag() != Default);
}
public:
circ_iterator() : c(nullptr, Default) {}
circ_iterator(circ& c_) : c(&c_, Default) {}
circ_iterator(const circ_iterator& other) : c(other.c) {}
};
circ_iterator begin(circ& c) { return circ_iterator(c); }
circ_iterator end(circ& c) { return circ_iterator(c, circ_iterator::end()); }