Какой самый простой способ пройтись и отключить std :: vector с помощью итераторов? - PullRequest
3 голосов
/ 11 марта 2009

У меня есть ситуация, когда я иду по вектору, что-то делаю:

std::vector::iterator iter = my_list.begin();

for ( ; iter != my_list.end(); ++iter )
{
  if ( iter->doStuff() )   // returns true if successful, false o/w
  {
    // Keep going...
  }
  else
  {
    for ( ; iter != m_list.begin(); --iter )  // ...This won't work...
    {
      iter->undoStuff();
    }
  }
}

В нормальных условиях - при условии, что все идет хорошо - я иду до my_list.end() и успешно завершаю цикл.

Однако, если что-то пойдет не так, пока я делаю что-то, я хочу иметь возможность отменить все - в основном, повторить мои шаги назад к самому началу вектора, отменяя все по одному в обратном порядке.

Моя проблема в том, что когда я добираюсь до my_list.begin() - как показано во вложенном цикле for - я действительно еще не закончил, потому что мне все еще нужно вызвать undoStuff() для моего первого элемента в списке. Теперь я мог бы просто сделать последний вызов вне цикла, но это кажется немного нечистым.

То, как я это вижу, я заканчиваю, только когда добираюсь до my_list.rend(). Однако я не могу сравнить std :: vector :: iterator с std :: vector :: reverse_iterator.

Учитывая то, что я пытаюсь сделать, каков наилучший выбор комбинации итератор-тип / цикл?

Ответы [ 9 ]

8 голосов
/ 11 марта 2009

Я немного заржавел, когда дело доходит до векторов STL, но возможно ли создать std::vector::reverse_iterator из вашего первоначального итератора? Тогда вам нужно будет только начать с последнего элемента, на котором вы были, когда будете двигаться вперед, и сможете сравнить его с my_list.rend(), чтобы убедиться, что первый элемент обработан.

4 голосов
/ 11 марта 2009

Хотя использование обратных итераторов через rbegin() и rend() работает хорошо, к сожалению, я нахожу, что преобразование между обратными и не обратными итераторами, как правило, довольно запутанно. Я никогда не могу вспомнить, не пройдя упражнение с логической головоломкой, нужно ли мне увеличивать или уменьшать до или после преобразования. В результате я обычно избегаю преобразования.

Вот способ, которым я бы, вероятно, кодировал ваш цикл обработки ошибок. Обратите внимание, что я думаю, что вам не нужно будет вызывать undoStuff() для итератора, который потерпел неудачу - в конце концов, doStuff() сказал, что это не удалось.

// handle the situation where `doStuff() failed...

// presumably you don't need to `undoStuff()` for the iterator that failed
// if you do, I'd just add it right here before the loop:
//
//     iter->undoStuff();

while (iter != m_list.begin()) {
    --iter;
    iter->undoStuff();
}
4 голосов
/ 11 марта 2009

Конечно, нет причин не использовать векторы operator[](), если это делает ваш код более понятным, простым и / или более эффективным.

2 голосов
/ 11 марта 2009

Это зависит от того, что делает ваша функция doStuff(), и насколько важна производительность в вашем контексте. Если это возможно, возможно, было бы яснее (т. Е. Читателю легче) работать с копией вашего вектора, и только если все в порядке, меняйте векторы.

std::vector<Foo> workingCopy;
workingCopy.assign(myVector.begin(), myVector.end());

bool success = true;
auto iter = workingCopy.begin();
for( ; iter != workingCopy.end() && success == true; ++iter )
    success = iter->doStuff();

if( success )
    myVector.swap(workingCopy);
2 голосов
/ 11 марта 2009

Не используя reverse_iterator, вы можете идти назад так:

while(iter-- != m_list.begin())
{
    iter->undoStuff();
}

Хотя это создает копию iter, стоимость не должна быть слишком большой. Вы можете рефакторинг для лучшей скорости:

while(iter != m_list.begin())
{
    --iter;
    iter->undoStuff();
}
1 голос
/ 11 марта 2009

Хорошо, я выйду на конечности здесь ..

std::vector iterator iter = my_list.begin();
bool error = false;

while(iter != my_list.end())
{
  error = !iter->doStuff();
  if(error)
    break
  else
    iter++;
}

if(error)
do
{
  iter->undoStuff();
  iter--;
} 
while(iter != my_list.begin())
1 голос
/ 11 марта 2009

Вам нужно использовать rbegin (), чтобы получить обратимый итератор.

Лично я все еще предпочитаю

for (int i=0;i<vecter.size();i++) { }
0 голосов
/ 27 сентября 2011

Это можно сделать с помощью reverse_iterator:

bool shouldUndo(false);
std::vector::iterator iter(my_list.begin()), end(my_list.end());
for ( ; iter != end && !shouldUndo; ++iter )
{
  shouldUndo = iter->doStuff();   // returns true if successful, false o/w
}
if (shouldUndo) {
  reverse_iterator<std::vector::iterator> riter(iter), rend(my_list.rend());
  //Does not call `undoStuff` on the object that failed to `doStuff`
  for ( ; riter != rend; ++riter )
  {
    iter->undoStuff();
  }
}
0 голосов
/ 11 марта 2009

Это то, что я называю инженерным делом, но это так весело

// This also can be done with adaptators I think
// Run DoStuff until it failed or the container is empty
template <typename Iterator>
Iterator DoMuchStuff(Iterator begin, Iterator end) {
  Iterator it = begin;
  for(; it != end; ++it) {
    if(!*it->DoStuff()) {
      return it;
    }
  }
  return it;
}

// This can be replaced by adaptators
template <typename Iterator>
void UndoMuchStuff(Iterator begin, Iterator end) {
  for(Iterator it = begin; it != end; ++it) {
    it->UndoStuff();
  }
}

// Now it is so much easier to read what we really want to do
typedef std::vector<MyObject*> MyList;
typedef MyList::iterator Iterator;
typedef MyList::reverse_iterator ReverseIterator;
Iterator it = DoMuchStuff(my_list.begin(), my_list.end());
if(it != my_list.end()) {
  // we need to unprocess [begin,it], ie including it
  UndoMuchStuff(ReverseIterator(1+it), ReverseIterator(my_list.begin()));
}
...