If your underlying container is simply a std::vector
, then you know that it's a contiguous container -- which actually makes this quite easy.
You can form an iterator from the following:
- A pointer (or reference) to the container being iterated, and
- The current iteration count (note: not 'index'). This will be used as "index" into the underlying container's
operator[]
after wrapping around the container's size()
.
The behavior of this iterator would be simply:
- Each increment just increments the current count
- Each dereference returns
(*elems_)[current_ % elems_->size()]
, which will account for the loop-around for the "period".
- The
begin()
would simply return an iterator with a 0
count, and
- The
end()
would return an iterator with a count of elems_.size() * nperiod_
An example of what this could look like as a LegacyForwardIterator
is the following:
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_;
};
The container would then define begin()
and end()
as:
PeriodicContainerIterator<Class> begin() {
return PeriodicContainerIterator<Class>{&elems_, 0};
}
PeriodicContainerIterator<Class> end() {
return PeriodicContainerIterator<Class>{&elems_, elems_->size() * nperiod_};
}
You could easily make this all the way up to a LegacyRandomAccessIterator
, but this requires a lot of extra functions which will bulk this answer.
If you don't specifically need this as an iterator but just want a simple way to visit each element in the periodic sequence, it might be easier to read / understand if you were to make this into a for_each
-like call that expects a callback instead. For example:
template <typename Fn>
void forEach(Fn&& fn)
{
for (auto i = 0; i < nperiod_; ++i) {
for (auto& e : elems_) {
fn(e);
}
}
}
Which allows for use like:
container.forEach([&](auto& e){
// 'e' is each visited element
});