考虑一个 STL 容器 C
这是可向前迭代的。我需要访问每个 step
元素,从idx
开始.如果C
是一个 vector (即有一个随机访问迭代器)我可以只使用索引算法:
template <class Container>
void go(const Container& C) {
for(size_t i = idx; i<C.size(); i+=step) {
/* do something with C[i] */
}
}
但是,如果 C
不支持,例如C
是一个列表,需要重写上面的解决方案。一个快速的尝试是:
template <class Container>
void go(const Container& C) {
size_t max = C.size();
size_t i = idx;
for(auto it = std::next(C.begin(),idx); i < max; i+=step, it+=step) {
/* do something with *it */
}
}
没多久就可以了……除了它很可能会触发未定义的行为。两者 std::next
和 it+=step
有可能超越C.end()
之前 i < max
执行检查。
与最初的 for 相比,我目前使用的解决方案(未显示)确实臃肿。我对第一次迭代和随后的迭代进行了单独检查。很多样板代码...
那么,我的问题是,上面的模式能否以安全、和简洁的方式编写?假设您想将这些循环嵌套 2 或 3 次。你不需要整页代码!
- 代码应该足够短
- 代码应该没有开销。做
std::next(C.begin(), i)
在i
的每次迭代中不必要的长,如果你能的话std::advance(it, step)
相反。 - 代码应该受益于
it
的情况std::advance
时确实是一个随机访问迭代器可以在恒定时间内执行。 -
C
是常数。我不插入、删除或修改C
在循环内。
最佳答案
您可以使用辅助函数:
template <typename IT>
IT secure_next(IT it, std::size_t step, IT end, std::input_iterator_tag)
{
while (it != end && step--) {
++it;
}
return it;
}
template <typename IT>
IT secure_next(IT it, std::size_t step, IT end, std::random_access_iterator_tag)
{
return end - it < step ? end : it + step;
}
template <typename IT>
IT secure_next(IT it, std::size_t step, IT end)
{
return secure_next(it, step, end, typename std::iterator_traits<IT>::iterator_category{});
}
然后:
for (auto it = secure_next(C.begin(), idx, C.end());
it != C.end();
it = secure_next(it, step, C.end()) {
/* do something with *it */
}
或者,使用 range-v3 ,你可以这样做:
for (const auto& e : C | ranges::view::drop(idx) | ranges::view::stride(step)) {
/* do something with e */
}
关于c++ - 如何以简洁的方式安全地访问容器中的每个第 n 个元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45823347/