Pergunta

Eu tenho uma situação onde eu estou marchando através de um vetor, fazer as coisas:

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();
    }
  }
}

Em condições normais - supondo que tudo vai bem -. Eu marcho todo o caminho até my_list.end() e terminar o circuito com sucesso

No entanto, se algo der errado, enquanto eu estou fazendo coisas, eu quero ser capaz de desfazer tudo -., Basicamente refazer meus passos de volta para o início do vetor, desfazendo tudo um de cada vez na ordem inversa

Meu problema é que quando eu chegar ao my_list.begin() - como mostrado na aninhadas para loop - Estou realmente não feito ainda porque eu ainda preciso undoStuff() chamada no meu primeiro elemento na lista. Agora, eu poderia apenas fazer a parte externa chamada final do circuito, mas isso parece um pouco sujo.

A forma como eu vejo, eu estou feito somente quando eu chegar ao my_list.rend(). No entanto, não se pode comparar um std :: vector :: iterator a um std :: vector :: reverse_iterator.

Dado o que eu estou tentando fazer, qual é a melhor escolha de iterador do tipo de combinação / loop?

Foi útil?

Solução

Enquanto estiver usando iteradores reversos via rbegin() e rend() funciona muito bem, infelizmente eu acho que a conversão entre reversa e não reverter iterarotrs tende a ser bastante confuso. Eu nunca consigo me lembrar sem ter que passar por um exercício de lógica quebra-cabeça se eu preciso para aumentar ou diminuir antes ou após a conversão. Como resultado, eu geralmente evitar a conversão.

Aqui está a maneira eu provavelmente codificar seu ciclo de tratamento de erros. Note que eu acho que você não teria que chamar undoStuff() para o iterador que falhou -. Afinal, doStuff() disse que não teve sucesso

// 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();
}

Outras dicas

Eu sou um pouco enferrujado quando se trata de vetores STL, mas seria possível criar um std::vector::reverse_iterator do seu iterador inicial? Então você só precisa começar no último item que estavam em quando vai para a frente, e seria capaz de compará-lo com my_list.rend() para se certificar de que o primeiro item é processado.

Há, naturalmente, nenhuma razão para não usar o operator[]() vetores se isso faz seu código mais claro, mais simples e / ou mais eficiente.

Depende do que a sua função doStuff() faz, e como é importante o desempenho está na sua contexto. Se possível, seria provavelmente mais clara (ie - mais fácil para o leitor). Para o trabalho em uma cópia do seu vector, e somente se está tudo bem, troque os vetores

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);

Sem usar um reverse_iterator, você pode andar para trás desta maneira:

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

Embora isso cria uma cópia do iter, o custo não deve ser muito grande. Você pode refatorar para melhor velocidade:

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

Você precisa usar rbegin () para obter um iterador reversível.

Pessoalmente eu ainda prefiro

for (int i=0;i<vecter.size();i++) { }

Ok, eu vou sair em um membro aqui ..

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())

Isto é o que eu chamo de mais de engenharia, mas é muito divertido

// 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()));
}

Isto pode ser feito com um 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();
  }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top