我会回避这个问题并使用包含合适的node<T>
成员链接范围。应对双向、侵入性
列表我会像这样使用不对称的node<T>:
template <typename T>
class intrusive::node
{
template <typename S, node<S> S::*> friend class intrusive::list;
template <typename S, node<S> S::*> friend class intrusive::iterator;
T* next;
node<T>* prev;
public:
node(): next(), prev() {}
node(node const&) {}
void operator=(node const&) {}
};
基本思想是 list<T, L> 包含一个 node<T> 使用
next 指向第一个元素的指针。这是相当的
直截了当:将指针 p 指向 T 指向下一个的链接
可以使用(p->*L).next 遍历节点。然而,而不是
使用T* 直接导航列表,实际上是iterator<T, L>
使用指向 node<T>: 的指针,而这对于
前向遍历,它支持后向遍历和插入
列表中的任何位置,无需对列表头进行特殊处理。
复制构造函数和复制赋值被定义为什么都不做
在复制节点时避免半插入节点。取决于
节点的需求可能更合理,而不是= delete
这些操作。但是,这与手头的问题无关。
迭代器只使用指向node<T> 的指针,其next
当前节点的成员点。对于第一个元素
列出这是指向list<T, L> 的node<T> 成员的指针。
假设您有一个指向合适的 node<T> 的指针,则可以从中创建 iterator<T,
L>:
template <typename T, intrusive::node<T> T::*Link>
class intrusive::iterator
{
template <typename S, node<S> S::*> friend class intrusive::list;
node<T>* current;
public:
explicit iterator(node<T>* current): current(current) {}
T& operator*() { return *this->operator->(); }
T* operator->() { return this->current->next; }
bool operator== (iterator const& other) const {
return this->current == other.current;
}
bool operator!= (iterator const& other) const {
return !(*this == other);
}
iterator& operator++() {
this->current = &(this->current->next->*Link);
return *this;
}
iterator operator++(int) {
iterator rc(*this);
this->operator++();
return rc;
}
iterator& operator--() {
this->current = this->current->prev;
return *this;
}
iterator operator--(int) {
iterator rc(*this);
this->operator--();
return rc;
}
};
取消引用只使用next 指针。对于
使用next 指针和
获取下一个node<T> 的地址的成员指针。
由于迭代器的prev 已经向后指向node<T>
迭代只需要将当前的node<T> 替换为
prev 元素。
最后,这留下了一个维护开头和结尾的列表
的名单。处理双向访问和相应的
访问最后一个节点会增加一些复杂性,并且需要
实际上有一个专用节点。这是一个实现(其中
没有经过彻底测试:我可能弄乱了一些链接):
template <typename T, intrusive::node<T> T::*Link>
class intrusive::list
{
node<T> content;
public:
list() { this->content.prev = &this->content; }
iterator<T, Link> begin() { return iterator<T, Link>(&this->content); }
iterator<T, Link> end() { return iterator<T, Link>(this->content.prev); }
T& front() { return *this->content.next; }
T& back() { return *(this->content.prev->prev->next); }
bool empty() const { return &this->content == this->content.prev; }
void push_back(T& node) { this->insert(this->end(), node); }
void push_front(T& node) { this->insert(this->begin(), node); }
void insert(iterator<T, Link> pos, T& node) {
(node.*Link).next = pos.current->next;
((node.*Link).next
? (pos.current->next->*Link).prev
: this->content.prev) = &(node.*Link);
(node.*Link).prev = pos.current;
pos.current->next = &node;
}
iterator<T, Link> erase(iterator<T, Link> it) {
it.current->next = (it.current->next->*Link).next;
(it.current->next
? (it.current->next->*Link).prev
: this->content.prev) = it.current;
return iterator<T, Link>(&(it.current->next->*Link));
}
};
只是为了有点理智:这是一个简单地打印列表的函数:
template <typename T, intrusive::node<T> T::*Link>
std::ostream& intrusive::operator<< (std::ostream& out, intrusive::list<T, Link>& list)
{
out << "[";
if (!list.empty()) {
std::copy(list.begin(), --list.end(), std::ostream_iterator<T>(out, ", "));
out << list.back();
}
return out << "]";
}
几乎没有其他方法可以避免做任何时髦的事情
访问封闭类。以上避免了几个条件。
假设我设法设置适当的链接更正代码
不会依赖任何实现定义或未定义的行为。
你会像这样使用列表:
class Node {
public:
intrusive::node<Node> link0;
intrusive::node<Node> link1;
int n;
Node(int n): n(n) {}
};
std::ostream& operator<< (std::ostream& out, Node const& n) {
return out << n.n;
}
int main()
{
intrusive::list<Node, &Node::link0> l0;
intrusive::list<Node, &Node::link1> l1;
Node n[] = { 10, 11, 12, 13, 14, 15 };
l0.push_front(n[0]);
l0.push_front(n[1]);
l0.push_front(n[2]);
l1.push_back(n[0]);
l1.push_back(n[1]);
l1.push_back(n[2]);
std::cout << "l0=" << l0 << " l1=" << l1 << "\n";
}