【发布时间】:2018-03-14 16:07:51
【问题描述】:
我封装了一个 std::list 以使其在迭代时可以安全地迭代,可能会将内容标记为“无效”,并且在迭代中会跳过“无效”内容。具体来说,在迭代过程中,当前对象可以安排自己或其他对象从列表中删除,并将这些对象标记为无效。然后定期清除列表中的无效对象。
如何定义增量运算符以使基于范围的 for 循环正常工作?这是我的课程:
template <typename T> class DeferredCleanupList
{
public:
DeferredCleanupList() {
(void)static_cast<Valid *>((T)0);
}
virtual ~DeferredCleanupList() {}
typedef typename std::list<T>::iterator iterator;
iterator begin() {
iterator it = container.begin();
if ((*it)->valid())
return it;
return next(it);
}
iterator next(iterator it) {
do {
++it;
}
while (it != end() && !(*it)->valid());
return it;
}
iterator end() { return container.end(); }
// to be implemented:
// typedef typename std::list<T>::const_iterator const_iterator ;
// const_iterator cbegin() const { return container.cbegin(); }
// const_iterator cend() const { return container.cend(); }
// const_iterator cnext() const { ??? }
size_t size() const { return container.size(); }
void add(T *ptr) { container.push_front(ptr); }
void remove(T *ptr) { ptr->invalidate(); }
// called occasionally
void delete_invalid() {
for (auto it = container.begin(); it != container.end(); ) {
auto ptr = *it;
if (ptr->valid())
++it;
else {
delete ptr;
it = container.erase(it);
}
}
}
private:
DeferredCleanupList(const DeferredCleanupList&);
DeferredCleanupList& operator=(const DeferredCleanupList&);
std::list<T> container;
};
我当前的测试用例是这样的:
int main() {
class D : public Valid {};
DeferredCleanupList<D *> list;
for (auto it = list.begin(); it != list.end(); it = list.next(it)); // works
for (auto ptr : list); // iterates, but doesn't call list.next(it)
}
编辑: 经过反复试验,我根据 cmets 中的建议编写了这个迭代器包装器:
template <typename T> class DeferredCleanupList
{
public:
class iterator {
public:
iterator(typename std::list<T>::iterator it, DeferredCleanupList<T>& ls) : it(it), list(ls) {}
iterator& operator=(const iterator& rhs) { it = rhs; return *this; }
iterator& operator++() {
do {
++it;
}
while (it != list.end().it && !(*it)->valid());
return *this;
}
friend bool operator==(const iterator& lhs, const iterator& rhs) { return lhs.it == rhs.it; }
friend bool operator!=(const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); }
T& operator*() { return *it; }
private:
typename std::list<T>::iterator it;
DeferredCleanupList& list;
};
iterator begin() {
iterator it = iterator(container.begin(), *this);
if (it == end() || (*it)->valid())
return it;
return ++it;
}
iterator end() { return iterator(container.end(), *this); }
}
它似乎在我扔给它的所有测试用例中都能完美运行。这种方法我是否遗漏了任何明显的东西?有没有更优雅的解决方案?
【问题讨论】:
-
使用
typedef typename std::list<T>::iterator iterator;无法覆盖预增量运算符,因为编译器无法区分容器的迭代器和标准列表迭代器。您需要创建一个在内部存储列表迭代器的包装类,然后重新定义预增量运算符 - 作为新类的方法,它需要看起来像iterator& operator++()。 -
另外,顺便提一下,如果您尝试在空容器上执行
begin()的当前实现可能会崩溃(即底层std::list<T>是空的)。 -
很好地抓住了 begin() 崩溃。我根据您的建议使用我提出的解决方案更新了我的问题。它工作得很好,我只是希望有一个更简单的解决方案。
-
Boost 提供了一个
boost::iterators::filter_iterator模板类,它做了很多你在解决方案中所做的事情,只是更普遍。使用它而不是自己滚动可能会使事情变得更简单,更容易阅读。
标签: c++ containers