如何以简洁的方式安全地访问容器中的每个第n个元素?

Cyg*_*sX1 10 c++ stl c++11

考虑一个C可向前迭代的STL容器.我需要从每个step元素开始访问idx.如果C是向量(即具有随机访问迭代器),我可以使用索引算法:

template <class Container>
void go(const Container& C) {
    for(size_t i = idx; i<C.size(); i+=step) {
        /* do something with C[i] */
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果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 */
    }
}
Run Code Online (Sandbox Code Playgroud)

不会太长,它的工作原理......除了很可能它会触发未定义的行为.双方std::nextit+=step有可能步的方式超越了C.end()之前i < max执行检查.

我最初使用的解决方案(未显示)与初始化相比真的很臃肿.我对第一次迭代和后面的迭代进行了单独检查.很多样板代码......

所以,我的问题是,上述模式能否以安全,简洁的方式编写?想象一下,你想要将这些循环嵌套2到3次.你不想要整个代码页面!

  • 代码应该相当短
  • 代码应该没有开销.这样std::next(C.begin(), i)在每次迭代过i是unnecessairly长,如果你可以std::advance(it, step)代替.
  • itstd::advance可以在恒定时间内执行时,代码应该受益于确实是随机访问迭代器的情况.
  • C是不变的.我不在C循环中插入,擦除或修改.

Jar*_*d42 11

您可以使用辅助函数:

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{});
}
Run Code Online (Sandbox Code Playgroud)

然后:

for (auto it = secure_next(C.begin(), idx, C.end());
     it != C.end();
     it = secure_next(it, step, C.end()) {
    /* do something with *it */
}
Run Code Online (Sandbox Code Playgroud)

或者,使用range-v3,您可以执行以下操作:

for (const auto& e : C | ranges::view::drop(idx) | ranges::view::stride(step)) {
    /* do something with e */
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*k B 7

关于需求的问题中的注释激发了我实现这一点,k * step而不是控制容器上的迭代次数的其他机制.

template <class Container>
void go(const Container& C)
{
    const size_t sz = C.size();

    if(idx >= sz) return;

    size_t k_max = (sz - idx) / step + 1;

    size_t k = 0
    for(auto it = std::advance(C.begin(), idx); k < k_max && (std::advance(it, step), true); ++k) {
        /* do something with *it */
    }
}
Run Code Online (Sandbox Code Playgroud)