如何确保迭代器不会越过末端()? 我一直在某些迭代器上使用Advance,但是我担心在端()上方可能的跨越。我想确保我的迭代器保持在范围之间,我想到了距离,但它看到了...

问题描述 投票:0回答:6
,但我害怕上面可能的跨越。我想确保我的迭代器保持在界限之间,我想到了

end()

distance
,但看来它并没有返回我期望的东西(迭代器iTerators overtass overpass
end()
#include <iostream>
#include <iterator>
#include <list>
using namespace std;

int main () {
  list<int> mylist;
  for (int i=0; i<10; i++) mylist.push_back (i*10);

  list<int>::const_iterator first = mylist.begin();
  const list<int>::const_iterator last = mylist.end();

  cout << "The distance is: " << distance(first,last) << endl; // 10
  advance(first, 10);
  cout << "The distance is: " << distance(first,last) << endl; // 0
  advance(first, 1);
  cout << "The distance is: " << distance(first,last) << endl; // 10
  advance(first, 10);
  cout << "The distance is: " << distance(first,last) << endl; // 0

  return 0;
}
)。您如何确保没有跨越?
The distance is: 10
The distance is: 0
The distance is: 10
The distance is: 0
there是输出:

  template <class Iter, class Incr>
  void safe_advance(Iter& curr, const Iter& end, Incr n)
  {
    size_t remaining(std::distance(curr, end));
    if (remaining < n)
    {
      n = remaining;
    }
    std::advance(curr, n);
  }

advance()past end()导致不确定的行为。您将必须在此片段时进行测试:
c++ iterator distance stdlist
6个回答
15
投票

您需要考虑当您不移动全部数量时会发生什么(

end()
返回为
distance


它在模型以外的任何其他内容上都无法拨打
advance


8
投票
:呼叫是o(n),其中n代表距离。

此外,

list
并不是真正的模型,因为它的方法不能保证是恒定的(甚至是摊销常数),实际上它很可能是O(n)。
看代码(如果您不能使用其他任何东西),则有两种可能性:
不使用前进,一次移动一个项目,并在每个步骤中与

Sequence

一起检查。

一开始,一劳永逸地划定大小,然后您知道在调用不确定的行为之前可以提前多少(您可以将计数器保持在侧面,包裹迭代器等...)


llet对此的行为:
size

不过,这保持这一数字很乏味...

Edit:

  • 补充大卫的应有的关注,以概括解决方案:
    list
  • 注意,对于双向迭代器,
  • end
  • 可在负距离上使用,但是这也需要引入
// Advance one at a time // replace calls to advance by this function typedef list<int>::const_iterator const_iterator; const_iterator safe_advance(const_iterator it, const_iterator end, size_t n) { for (size_t i = 0; i != n && it != end; ++i) { ++it; } return it; } // Precompute size size_t const size = list.size(); size_t remaining = size; const_iterator begin = list.begin(); size_t ad = std::min(10, remaining); advance(begin, ad); remaining -= ad; ad = std::min(1, remaining); advance(begin, ad); remaining -= ad;

,并且会变得非常乏味。因此,我更喜欢第二种方法:

// Advance `it` from n, or up to end
// returns the number of steps that could not be performed
template <typename Iterator>
size_t safe_advance(Iterator& it, Iterator const& end, size_t n)
{
  size_t i = 0;
  for (; i != n && it != end; ++i, ++it);
  return n - i;
}

最后,尽管我不会在这里这样做,但最好将模板专业用于

advance

模板,因为这些操作可以用这样的操作在o(1)中进行。 distance无法做到这一点。当您的容器不提供随机访问时,它试图通过前进的启动来达到结束,这将在开始超过最后一次时会造成破坏。

您必须从一个有效的起点开始(即,通过前进的开始,您可以到达最后),并在每个进步之前检查距离,并且仅通过x

前进

对于一件事,您还可以编写一个迭代器包装器,该包装器存储最终迭代器并检查关键操作。

为了使用Boost's

begin
示例
template <typename BI>
size_t safe_reverse(BI& it, BI const& begin, size_t n)
{
  for (; n != 0 && it != begin; --n, --it);
  return n;
}

RandomAccessIterator
iterator_facade

#include <boost/iterator/iterator_facade.hpp>
#include <iterator>
#include <stdexcept>

template <class Iter>
class checked_iterator:
    public boost::iterator_facade<
        checked_iterator<Iter>,
        typename std::iterator_traits<Iter>::value_type,
        typename std::iterator_traits<Iter>::iterator_category
    >
{
    Iter it, end;
public:
    checked_iterator(Iter it, Iter end): it(it), end(end) {} 
    void increment() 
    { 
        if (it == end) { throw std::range_error("checked_iterator: increment beyond end"); }
        ++it;
    }
    void decrement()
    {
        //TODO: this is not checked
        --it;
    }
    bool equal(const checked_iterator& other) const
    {
        return it == other.it;
    }
    typename std::iterator_traits<Iter>::reference dereference() const 
    {
        if (it == end) { throw std::range_error("checked_iterator: dereference end"); }
        return *it;
    }
    void advance(typename std::iterator_traits<Iter>::difference_type n)
    {
        //TODO: not checked for underflow
        if (std::distance(it, end) < n) { throw std::range_error("checked_iterator: advance beyond end"); }
        it += n;
    }
    typename std::iterator_traits<Iter>::difference_type distance_to(const checked_iterator& other) const
    {
        return other.it - it;
    }
    Iter base() const { return it; }
};

//create checked_iterators from containers, could be overloaded for arrays
template <class Container>
checked_iterator<typename Container::iterator> checked_begin(Container& c)
{
    return checked_iterator<typename Container::iterator>(c.begin(), c.end());
}

template <class Container>
checked_iterator<typename Container::const_iterator> checked_begin(const Container& c)
{
    return checked_iterator<typename Container::const_iterator>(c.begin(), c.end());
}

template <class Container>
checked_iterator<typename Container::iterator> checked_end(Container& c)
{
    return checked_iterator<typename Container::iterator>(c.end(), c.end());
}

template <class Container>
checked_iterator<typename Container::const_iterator> checked_end(const Container& c)
{
    return checked_iterator<typename Container::const_iterator>(c.end(), c.end());
}

示例测试代码:

5
投票
#include <vector> #include <list> #include <iostream> int main() { typedef std::list<int> Container; try { Container v(10); checked_iterator<Container::iterator> it = checked_begin(v); std::advance(it, 6); std::cout << *it << '\n'; std::advance(it, 4); std::cout << *it << '\n'; const Container& r = v; checked_iterator<Container::const_iterator> cit = checked_begin(r); std::advance(cit, 11); } catch (const std::exception& e) { std::cout << e.what() << '\n'; } }

为了实现<=(distance(first,last) in order not to overtake last.

safe_advance

3
投票

std::advance


很简单。为了避免超越
#include <iterator>
#include <stdexcept>

namespace detail
{
    template <class Iter>
    void safe_advance_aux(
        Iter& it, Iter end, 
        typename std::iterator_traits<Iter>::difference_type n, 
        std::random_access_iterator_tag
        )
    {
        if (end - it < n) throw std::range_error("advance beyond end (ra)");
        it += n;
    }

    template <class Iter, class Tag>
    void safe_advance_aux(
        Iter& it, Iter end, 
        typename std::iterator_traits<Iter>::difference_type n, 
        Tag
        )
    {
        for (typename std::iterator_traits<Iter>::difference_type i = 0; i != n; ++i) {
            if (it == end) throw std::range_error("advance beyond end");
            ++it;
        }
    }
}

template <class Iter>
void safe_advance(Iter& it, Iter end, typename std::iterator_traits<Iter>::difference_type n)
{
    detail::safe_advance_aux(it, end, n, typename std::iterator_traits<Iter>::iterator_category());
}

.end()

避免避免超越
.end()
。这种情况也没有什么不同,因为避免使用
NULL

指针等。构建代码,以免发生这种情况。例如。使用使用诸如

it != v.end()
等条件的循环。一些流行的标准库实现会检测到这些错误并告诉您,但您不应该依靠它,只能在测试/调试期间使用它。
update

如果您不能确定自己可以大于一个大的数量,那么您根本不得超过一个以上。


3
投票
不能以可能导致过去的方式使用

advance()

。当您的当前迭代器指向结束时,您永远不会使用增量(一种特殊的预付款形式),因此,除非您的代码已经知道容器中剩余的元素,否则您也绝不应该使用Advance。如果不确定,您需要一次增加一个,并在每个项目上检查结束。 
advance()
没有(也不能)为您进行任何检查,因为这将是不需要该功能的代码的性能。

INSTEAD,检查您的代码,并弄清楚为什么打电话给
advance
会导致其在容器的末端运行,然后用代码解决该问题。

更多的背景可能会给我们提供更好的帮助。

不使用迭代器;代替使用

end

。它具有一个安全的方法,可以推进子量的开始,并且不会让它超越终点。当您要访问“迭代器”时,只需使用

advance

(或

advance
)方法即可。它可能会增加一些不便,但是我认为它不仅更安全,而且在处理范围/视图而不是与迭代器的情况下更有意义。作为奖励,您始终可以通过“迭代器”(即子范围)访问范围的末端,并且,由于您的子量距离是A
ranges::subrange
,因此它可以与视图API透明地播放,例如您可以将其送至范围适应器。


2
投票
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.