我一直在某些advance
上使用iterators
,但我担心可能会超过end()
。我想确保我的迭代器保持在界限之间,我想到了distance
,但似乎它没有返回我期望的结果(迭代器超越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
答案 0 :(得分:14)
advance()last end()导致未定义的行为。你将不得不按照这个片段进行测试:
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);
}
你需要考虑当你没有移动全部金额时发生的事情(curr
返回end()
。
答案 1 :(得分:8)
在除distance
模型之外的任何内容上调用advance
或RandomAccessIterator
效率低下:调用为O(n),其中n表示距离。
此外,list
并非真正的Sequence
模型,因为其size
方法不能保证不变(或甚至是摊销常数),实际上它可能完全是为O(n)。
查看代码(如果你不能使用除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
可用于负距离,但这也需要引入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
的模板会很好,因为这些操作可以在O(1)中完成。
答案 2 :(得分:4)
距离不能这样做。当你的容器不提供随机访问时,它会尝试通过提前启动来达到目的,这会在最后一次开始超时时造成破坏。
你必须从一个有效的起点开始(即通过提前开始你可以到达最后)并在每次前进之前检查距离并且仅前进X <=(距离(第一个,最后一个)以便不超过最后一个。
答案 3 :(得分:3)
很简单。为避免超出.end()
,请避免超越.end()
。情况没有什么不同,因为避免使用NULL
指针等。构造您的代码,以便它永远不会发生。例如。使用使用it != v.end()
等条件的循环。一些流行的标准库实现检测到这些错误并告诉您,但您不应该依赖它,只在测试/调试期间使用它。
如果您无法确定advance()
的数量大于1,那么您不能advance()
多于一个。
答案 4 :(得分:2)
我认为你在这里试图解决错误的问题。
请勿以可能导致超过advance
的方式使用end
。当你的当前迭代器指向结束时,你永远不会使用递增(一种特殊的推进形式),所以你应该永远不要使用advance,除非你的代码已经知道容器中有足够的剩余元素。如果您不确定,则需要逐个增加一个并检查每个项目的结束。 advance
不会(也不能)对您进行任何检查,因为对于不需要该功能的代码而言,它会受到性能损失。
相反,请检查您的代码并弄清楚为什么调用advance
可能导致它在容器的末尾运行,而是用代码修复该问题。
更多的背景可能会给我们更好的帮助机会。
答案 5 :(得分:2)
首先,您还可以编写一个迭代器包装器来存储结束迭代器并检查关键操作。
例如,为简洁而使用boost 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());
}
示例测试代码:
#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';
}
}
至于实现safe_advance
函数,这也可以区分随机访问迭代器和其他函数,例如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());
}