Я пытаюсь найти лучший способ определить, нахожусь ли я на последней итерации цикла по карте, чтобы сделать что-то вроде следующего:
for (iter = someMap.begin(); iter != someMap.end(); ++iter) {
bool last_iteration;
// do something for all iterations
if (!last_iteration) {
// do something for all but the last iteration
}
}
Кажется, есть несколько способов сделать это: итераторы произвольного доступа, функция distance и т. д. Что такое канонический метод?
Обновлено: нет итераторов произвольного доступа для карт!





Это кажется самым простым:
bool last_iteration = iter == (--someMap.end());
Это не работает ... нет совпадения для оператора [тип итератора] + int!
Для двунаправленных итераторов (которые есть на карте) не определено +, чтобы управлять им.
Определено уменьшение итератора до пустой коллекции?
@Tim: Если это пустая коллекция, мы никогда не войдем в тело цикла. :-)
Вы можете просто вытащить элемент из карты до итерации, затем выполнить свою «последнюю итерацию» из цикла, а затем вернуть элемент в в карту. Это ужасно плохо для асинхронного кода, но, учитывая, насколько плох остальной C++ для параллелизма, я не думаю, что это будет проблемой. :-)
Карты ЗАКАЗЫВАЮТСЯ - может вы думаете о хэше?
У меня создалось впечатление, что Карты были реализованы по умолчанию с использованием хэш-таблиц. Подправлю ответ. Спасибо!
Канонический? Я не могу утверждать это, но я бы предложил
final_iter = someMap.end();
--final_iter;
if (iter != final_iter) ...
Отредактировано, чтобы исправить, как предлагает KTC. (спасибо! Иногда вы делаете слишком быстро и ошибаетесь в самых простых вещах ...)
Декремент должен быть предварительным, иначе finalIter будет концом.
ПРЕДОСТЕРЕЖЕНИЕ: для этого требуются двунаправленные итераторы, поэтому он не работает во всех классах коллекций STL.
Вы можете просто сравнить со значением в rbegin ()
@ffpf, вы не можете, потому что сравнения между прямыми итераторами и обратными итераторами не работают.
Можно сравнить с - (someMap.rbegin (). Base ()), но это просто усложняет задачу без уважительной причины. (Это было то, что изначально было задумано опубликовать LOL: D)
Конечно, ну! Признаюсь, с момента моей последней крупной работы над C++ прошло совсем немного времени. Удивительно, как быстро вещи покидают мозг, когда ими не пользуются.
Если карта не изменяется внутри цикла, final_iter следует оценивать вне его. В этом случае карту необходимо проверить на пустоту, чтобы не столкнуться с пугающим неопределенным поведением.
Изменен Mark Ransom, так что он действительно работает так, как задумано.
finalIter = someMap.end();
--finalIter;
if (iter != final_iter)
Простой, но эффективный подход:
size_t items_remaining = someMap.size();
for (iter = someMap.begin(); iter != someMap.end(); iter++) {
bool last_iteration = items_remaining-- == 1;
}
Полная программа:
#include <iostream>
#include <list>
void process(int ii)
{
std::cout << " " << ii;
}
int main(void)
{
std::list<int> ll;
ll.push_back(1);
ll.push_back(2);
ll.push_back(3);
ll.push_back(4);
ll.push_back(5);
ll.push_back(6);
std::list<int>::iterator iter = ll.begin();
if (iter != ll.end())
{
std::list<int>::iterator lastIter = iter;
++ iter;
while (iter != ll.end())
{
process(*lastIter);
lastIter = iter;
++ iter;
}
// todo: think if you need to process *lastIter
std::cout << " | last:";
process(*lastIter);
}
std::cout << std::endl;
return 0;
}
Эта программа дает:
1 2 3 4 5 | last: 6
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <algorithm>
using namespace boost::lambda;
// call the function foo on each element but the last...
if ( !someMap.empty() )
{
std::for_each( someMap.begin(), --someMap.end(), bind( &Foo, _1 ) );
}
Использование std :: for_each гарантирует, что цикл будет плотным и точным ... Обратите внимание на введение функции foo (), которая принимает единственный аргумент (тип должен соответствовать тому, что содержится в someMap). К этому подходу добавлено добавление одной строки. Конечно, если Foo действительно маленький, вы можете использовать лямбда-функцию и избавиться от вызова & Foo.
Это не позволяет «делать что-то для всех итераций».
Если вы просто хотите использовать ForwardIterator, это должно сработать:
for ( i = c.begin(); i != c.end(); ) {
iterator cur = i++;
// do something, using cur
if ( i != c.end() ) {
// do something using cur for all but the last iteration
}
}
Вот мой оптимизированный вариант:
iter = someMap.begin();
do {
// Note that curr = iter++ may involve up to three copy operations
curr = iter;
// Do stuff with curr
if (++iter == someMap.end()) {
// Oh, this was the last iteration
break;
}
// Do more stuff with curr
} while (true);
Удивлен, что об этом пока никто не упомянул, но конечно в бусте что-то есть;)
Boost.Next (и эквивалент Boost.Prior)
Ваш пример будет выглядеть так:
for (iter = someMap.begin(); iter != someMap.end(); ++iter) {
// do something for all iterations
if (boost::next(iter) != someMap.end()) {
// do something for all but the last iteration
}
}
Я почти уверен, что сейчас он есть в стандартной библиотеке как std::next и т. д.
Следующий код будет оптимизирован компилятором, чтобы быть лучшим решением этой задачи как по производительности, так и по правилам ООП:
if (&*it == &*someMap.rbegin()) {
//the last iteration
}
Это лучший код по правилам ООП, потому что std :: map имеет специальную функцию-член rbegin для такого кода, как:
final_iter = someMap.end();
--final_iter;
Зачем работать, чтобы найти EOF, чтобы ничего ему не дать.
Просто исключите это;
for (iter = someMap.begin(); someMap.end() - 1; ++iter) {
//apply to all from begin to second last element
}
ПОЦЕЛУЙ (ДЕЛАЙТЕ ЭТО ПРОСТОЙ)
Вы уверены, что это работает? Определяют ли итераторы operator- с int?
Как насчет этого, никто не упоминает, но ...
for (iter = someMap.begin(); iter != someMap.end(); ++iter) {
// do something for all iterations
if (iter != --someMap.end()) {
// do something for all but the last iteration
}
}
это кажется простым, мм ...
Начиная с C++ 11, вы также можете использовать std :: next ()
for (auto iter = someMap.begin(); iter != someMap.end(); ++iter) {
// do something for all iterations
if (std::next(iter) != someMap.end()) {
// do something for all but the last iteration
}
}
Хотя вопрос был задан некоторое время назад, я подумал, что им стоит поделиться.
Для тех, кто любит цикл на основе диапазонов C++ 11:
for (const auto& pair : someMap) {
if (&pair != &*someMap.rbegin()) ...
}
Обратите внимание, что здесь работает только ссылочный тип, а не auto pair.
Действительно ли итераторы определяют арифметический плюс с целыми числами? Я так не думал; но даже если некоторые из них это сделают, я уверен, что вы не можете полагаться на него для контейнера каждый.