我有一个由std :: find()生成的stl迭代器,并希望测试它是否是最后一个元素。写这个的一种方法如下:
mine *match = someValue;
vector<mine *> Mine(someContent);
vector<mine *>::iterator itr = std::find(Mine.begin(), Mine.end(), match);
if (itr == --Mine.end()) {
doSomething;
}
但在我看来,递减end()迭代器是在寻找麻烦,例如如果向量没有元素,那么它将是未定义的。即使我知道它永远不会是空的,它仍然看起来很难看。我想也许rbegin()是可行的方法,但我不确定将前向迭代器与反向迭代器进行比较的最佳方法。
答案 0 :(得分:54)
这样做:
// defined in boost/utility.hpp, by the way
template <typename Iter>
Iter next(Iter iter)
{
return ++iter;
}
// first check we aren't going to kill ourselves
// then check if the iterator after itr is the end
if ((itr != Mine.end()) && (next(itr) == Mine.end()))
{
// points at the last element
}
就是这样。永远不会给你未定义的行为,适用于所有迭代器,美好的一天。
包装好玩:
template <typename Iter, typename Cont>
bool is_last(Iter iter, const Cont& cont)
{
return (iter != cont.end()) && (next(iter) == cont.end())
}
,并提供:
if (is_last(itr, Mine))
如果您对实用功能/漂亮的代码过敏,请执行以下操作:
if ((itr != Mine.end()) && (itr + 1 == Mine.end()))
但是你不能在非随机访问迭代器上做到这一点。这个适用于双向迭代器:
if ((itr != Mine.end()) && (itr == --Mine.end()))
首次检查后end() > itr
是安全的。
答案 1 :(得分:11)
是的,如果向量可能为空,则递减(或递增)end
是不安全的。
为了确保安全,请使用已知安全有效的减法和值:
if ( Mine.end() - itr == 1 )
为了兼容所有前向迭代器(例如slist
,而不是vector
和deque
的随机访问迭代器),请使用
if ( std::distance( itr, Mine.end() ) == 1 )
或者如果你关心性能但是有双向迭代器(包括任何C ++ 03容器)
if ( itr != Mine.end() && itr == -- Mine.end() )
或只有前向迭代器和O(1)时间的真正肛门案例,
if ( itr != Mine.end() && ++ container::iterator( itr ) == Mine.end() )
或者如果你对聪明有所帮助以避免命名迭代器类,
if ( itr != Mine.end() && ++ ( Mine.begin() = itr ) == Mine.end() )
答案 2 :(得分:5)
为什么只有当项目是最后一项时才需要做特殊行为?
这个怎么样?该计划只是将迭代器项目的地址与容器中最后一项的地址进行比较,并检查以确保该项目实际上不是结束(使back
调用安全): / p>
if (itr != Mine.end() && &*itr == &Mine.back()) {
doSomething;
}
答案 3 :(得分:3)
如果你这样做:
if(itr != Mine.end() && itr == --Mine.end())
应该没问题。因为如果itr不在最后,那么容器中必须至少有一个元素,因此当递减时,end必须产生一个值结果。
但如果你仍然不喜欢这样,有很多方法可以做同等的事情,正如所有其他答案所显示的那样。
这是另一种选择:
if(itr != Mine.end() && std::distance(Mine.begin(), itr) == Mine.size()-1)
答案 4 :(得分:3)
首先需要determine if an iterator is a reverse one的方式,ingeniously shown here:
#include <iterator>
#include <type_traits>
template<typename Iter>
struct is_reverse_iterator : std::false_type { };
template<typename Iter>
struct is_reverse_iterator<std::reverse_iterator<Iter>>
: std::integral_constant<bool, !is_reverse_iterator<Iter>::value>
{ };
然后你可以有两种口味来进行测试
template<bool isRev> // for normal iterators
struct is_last_it
{
template<typename It, typename Cont>
static bool apply(It it, Cont const &cont)
{ // you need to test with .end()
return it != cont.end() && ++it == cont.end();
}
};
template<> // for reverse iterators
struct is_last_it<true>
{
template<typename It, typename Cont>
static bool apply(It it, Cont const &cont)
{ // you need to test with .rend()
return it != cont.rend() && ++it == cont.rend();
}
};
单一界面功能
template<typename It, typename Cont>
bool is_last_iterator(It it, Cont const &cont)
{
return is_last_it<is_reverse_iterator<It>::value>::apply(it, cont);
};
然后,对于每种类型的迭代器(反向/直线),您都可以使用接口函数
int main()
{
std::vector<int> v;
v.push_back(1);
auto it (v.begin()), ite(v.end()); // normal iterators
auto rit(v.rbegin()), rite(v.rend()); // reverse iterators
std::cout << is_last_iterator(it, v) << std::endl;
std::cout << is_last_iterator(ite, v) << std::endl;
std::cout << is_last_iterator(rit, v) << std::endl;
std::cout << is_last_iterator(rite, v) << std::endl;
return 0;
}
请注意,除了std::begin()
和std::end()
之外,还有一些常见功能,包括std::rbegin()
和std::rend()
。如果可能,请使用此功能集代替成员.begin()
等。
答案 5 :(得分:2)
这是另一个潜在的解决方案:
template<class Iterator, class Container> bool is_last(Iterator it, const Container& cont)
{
// REQUIREMENTS:
// the iterator must be a valid iterator for `cont`
if( it == cont.end() )
return false; // or throw if you prefer
return (++it) == cont.end();
}
答案 6 :(得分:1)
更好的方法是复制迭代器然后递增它。然后,您可以针对end()
测试递增版本。如果您小心,可以使用后增量来避免正式复制它。
if (++vector<mine*>::iterator(itr) == Mine.end())
如果它已经在最后:
if (itr == Mine.end() || ++vector<mine*>::iterator(itr) == Mine.end())
或者,基于GMan的答案,但更安全一点:
if (Mine.Length() == 0 || itr == Mine.End() || &*itr == &Mine.back())
我刚刚修复了最后一个,因为我错了&*
。
答案 7 :(得分:1)
这与从单链表中删除节点基本相同。你必须有两个迭代器,一个跟在另一个节点后面的一个节点,所以当“forward”迭代器到达你想要删除的节点(或者任何操作;在你的情况下,所需的节点将是结束),“以下“迭代器之前指向节点(在您的情况下,这将是最终节点)。
答案 8 :(得分:1)
尽量使这个答案变得简单和多样化:
if( itr!=Mine.end() && itr== --Mine.end())
如果迭代器不是双向的,
if( itr!=Min.end() && ++decltype(itr)(itr)==Mine.end())
第二个创建itr的临时副本并将其递增以测试结束迭代器。
在这两种情况下,第一次测试都会避免空容器触发未定义的情况。