I have an stl iterator resulting from a std::find() and wish to test whether it is the last element. One way to write this is as follows:
mine *match = someValue;
vector<mine *> Mine(someContent);
vector<mine *>::iterator itr = std::find(Mine.begin(), Mine.end(), match);
if (itr == --Mine.end()) {
doSomething;
}
But it seems to me that decrementing the end() iterator is asking for trouble, such as if the vector has no elements, then it would be undefined. Even if I know it will never be empty, it still seems ugly. I'm thinking that maybe rbegin() is the way to go, but am not certain as to best way to compare the forward iterator with a reverse iterator.
Сделайте это:
// 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
.
Вот еще одно потенциальное решение:
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();
}
Да, уменьшать (или увеличивать) 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() )
Почему вам нужно делать специальное поведение, только если элемент последний?
Как насчет этого. Планируется просто сравнить адрес элемента итератора с адресом последнего элемента в контейнере, с проверкой, чтобы убедиться, что элемент не является последним (что делает вызов back
безопасным):
if (itr != Mine.end() && &*itr == &Mine.back()) {
doSomething;
}
Если вы это сделаете:
if(itr != Mine.end() && itr == --Mine.end())
Все будет в порядке. Потому что, если itr не в конце, тогда в контейнере должен быть хотя бы 1 элемент, и поэтому конец должен давать результат значения при уменьшении.
Но если вам это все еще не нравится, есть много способов сделать что-то эквивалентное, как показывают все остальные ответы.
Вот еще одна альтернатива:
if(itr != Mine.end() && std::distance(Mine.begin(), itr) == Mine.size()-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())
Я только что исправил последний, так как ошибался насчет & *
.