用于封装标准容器的重载operator ++

时间:2018-03-14 16:07:51

标签: c++ containers

我封装了一个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)
}

编辑: 经过一些试验和错误,我根据评论中的建议编写了这个迭代器包装器:

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); }
}

它似乎在我抛出的所有测试用例中都能很好地工作。我错过了这种方法的任何明显的东西吗?有更优雅的解决方案吗?

2 个答案:

答案 0 :(得分:0)

来自http://en.cppreference.com/w/cpp/language/range-for

  

以上语法产生的代码等效于以下(__ range,   __begin和__end仅用于展示):

{
    auto && __range = range_expression ; 
    for (auto __begin = begin_expr, __end = end_expr; 
            __begin != __end; ++__begin) { 
        range_declaration = *__begin; 
    loop_statement 
    } 
}

即你必须定义preincrement运算符。

答案 1 :(得分:0)

您无法在容器类中执行此操作。您应该实现自己的迭代器并在其前缀增量运算符中实现所需的行为。您可以通过多种方式执行此操作,包括继承 STL 中的一个或为 std :: list 迭代器创建自己的包装器。

实际上要在迭代器中实现所需的行为,你需要提供容器的自定义迭代器类 end iterator

除非你的设计真的很好用,否则我不建议你实现自己的迭代器。不必要的并发症几乎总会导致更多不必要的并发症。

我修改了您的代码,因此它实际上编译并实现了 std :: list 迭代器包装器,以按照您的计划行事。

#include <iostream>
#include <list>
#include <iterator>

using namespace std;

class Valid {};

template<typename _Tp>
struct myIteratorWrapper
{

  typedef myIteratorWrapper<_Tp>         _Self;
  typedef _Tp               value_type;
  typedef _Tp*                 pointer;
  typedef _Tp&               reference;

  typedef typename std::list<_Tp>::iterator listIterator;

  listIterator it;
  listIterator itEnd;

  myIteratorWrapper(const listIterator& listIterArg) _GLIBCXX_NOEXCEPT
    : it(listIterArg)
  {}

  myIteratorWrapper(const listIterator& itBegin,
            const listIterator& itEnd) _GLIBCXX_NOEXCEPT
    : it(itBegin), itEnd(itEnd)
  {}

  reference
  operator*() const _GLIBCXX_NOEXCEPT
  { return *it; }

  pointer
  operator->() const _GLIBCXX_NOEXCEPT
  { return &(*it); }

  /* Change logic of this method as you wish, but keep the signature */ 
  _Self&
  operator++() _GLIBCXX_NOEXCEPT
  {
    do
    {
      ++it;
    }while (it != itEnd && !(*it)->valid());

    return *this;
  }

  bool
  operator==(const _Self& __x) const _GLIBCXX_NOEXCEPT
  { return it == __x.it; }

  bool
  operator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT
  { return it != __x.it; }
};

template <typename T> class DeferredCleanupList
{
public:
  DeferredCleanupList() {
    (void)static_cast<Valid *>((T)0);
  }
  virtual ~DeferredCleanupList() {}

  typedef myIteratorWrapper<T> iterator;

  iterator begin() {
    iterator it(container.begin(), container.end());
    return it;
  }

  iterator next(iterator it) {       
    return ++it;
  }

  iterator end() { return container.end(); }

  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;
};

class D : public Valid {
  bool isValid;
  std::string myName;
public:

  D(std::string myName, bool arg = false)
    : myName(myName), isValid(arg) {}

  bool valid() const
  { return isValid; }

  const std::string& whoAmI() const
  { return myName; }
};

int main() {
  D o1("o1", true);
  D o2("o2");
  D o3("o3", true);
  D o4("o4");
  D o5("o5", true);

  DeferredCleanupList<D *> list;
  list.add(&o1);
  list.add(&o2);
  list.add(&o3);
  list.add(&o4);
  list.add(&o5);

  for (auto ptr : list)
    {
      std::cout << ptr->whoAmI() << std::endl;
    }
}

输出:

o5
o3
o1