自定义容器上的C ++“定期”迭代器

问题描述

我有一个包含特定类元素向量的类。主要思想是根据序列的一个周期(elems_和周期数(nperiod_生成元素的周期性序列,因此我不需要存储所有元素,但只有一个时期。

class PeriodicContainer
{
private:
  std::vector<Class> elems_; // elements
  size_t nperiod_; // period of repetition of elems_
public:
  PeriodicContainer();
  PeriodicContainer(const std::vector<Class>& elems,size_t nperiod);
  /*...*/
}

是否可以为PeriodicContainer实现自定义迭代器,以便我可以执行(半伪代码)之类的事情:

PeriodicContainer container({Class(1),Class(2)},4);
for (auto it : container)
  std::cout << it << '\n';

输出将是

Class(1)
Class(2)
Class(1)
Class(2)
Class(1)
Class(2)
Class(1)
Class(2)

解决方法

如果可以使用range-v3,则可以执行以下操作:

namespace rv = ranges::views;    

std::vector<Class> Container { Class(1),Class(2) };

for (auto it : rv::repeat_n(Container,4) | rv::join)
    std::cout << it;

,而不必自己编写任何其他代码。这同样适用于任何连续的容器,而不仅限于std::vector

这里是demo

,

如果您的基础容器只是一个std::vector,则您知道它是一个连续的容器-实际上,这很容易。

您可以通过以下方式形成迭代器:

  • 指向要迭代的容器的指针(或引用),并且
  • 当前迭代计数(注意:不是“索引”)。包装在容器的operator[]周围后,它将用作基础容器的size()的“索引”。

此迭代器的行为很简单:

  • 每个增量只会增加当前计数
  • 每个取消引用都返回(*elems_)[current_ % elems_->size()],这将说明“期间”的循环。
  • begin()只会返回一个计数为0的迭代器,并且
  • end()将返回一个计数为elems_.size() * nperiod_的迭代器

下面是一个LegacyForwardIterator的示例:


template <typename T>
class PeriodicContainerIterator
{
public:
    using value_type = T;
    using reference = T&;
    using pointer = T*;
    using difference_type = std::ptrdiff_t;
    using iterator_category = std::forward_iterator_tag;
 
    PeriodicContainerIterator(std::vector<T>* elems,std::size_t current)
        : elems_{elems},current_{current}
    {}

    reference operator*() {
        return (*elems_)[current_ % elems_->size()]
    }
    pointer operator->() {
        return &(*elems_)[current_ % elems_->size()];
    }
    PeriodicContainerIterator& operator++() const {
        ++current_;
        return (*this);
    }
    PeriodicContainerIterator operator++(int) const {
        auto copy = (*this);
        ++current_;
        return copy;
    }
 
    bool operator==(const PeriodicContainerIterator& other) const {
        return current_ == other.current_;
    }
    bool operator!=(const PeriodicContainerIterator& other) const {
        return current_ != other.current_;
    }
    
private:
    std::vector<T>* elems_;
    std::size_t current_;
};

然后,容器将begin()end()定义为:


PeriodicContainerIterator<Class> begin() {
    return PeriodicContainerIterator<Class>{&elems_,0};
}
PeriodicContainerIterator<Class> end() {
    return PeriodicContainerIterator<Class>{&elems_,elems_->size() * nperiod_};
}

您可以很容易地将其一直扩展到LegacyRandomAccessIterator,但是这需要大量额外的功能,这些功能会占用大量答案。


如果您不是专门作为 iterator 的对象,而只是想以一种简单的方式来访问周期序列中的每个元素,那么如果要使它更容易阅读/理解到类似for_each的调用中,而该调用需要回调。例如:

template <typename Fn>
void forEach(Fn&& fn)
{
    for (auto i = 0; i < nperiod_; ++i) {
        for (auto& e : elems_) {
            fn(e);
        }
    }
}

允许使用的方式如下:

container.forEach([&](auto& e){
    // 'e' is each visited element
});