问题描述
我有一个包含特定类元素向量的类。主要思想是根据序列的一个周期(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
});