2010-04-23 22 views
9

Estoy intentando construir una cadena usando elementos de datos almacenados en una lista std ::, donde quiero que las comas se coloquen solo entre los elementos (es decir, si los elementos son {A, B , C, D} en la lista, la cadena resultante debe ser "a, B, C, D"std :: list iterator: obtener el siguiente elemento

Este código no funciona:.

typedef std::list< shared_ptr<EventDataItem> > DataItemList; 
// ... 
std::string Compose(DataItemList& dilList) 
{ 
    std::stringstream ssDataSegment; 
    for(iterItems = dilList.begin(); 
     iterItems != dilList.end(); 
     iterItems++) 
    { 
     // Lookahead in list to see if next element is end 
     if((iterItems + 1) == dilList.end()) 
     { 
      ssDataSegment << (*iterItems)->ToString(); 
     } 
     else 
     { 
      ssDataSegment << (*iterItems)->ToString() << ","; 
     } 
    } 
    return ssDataSegment.str(); 
} 

¿Cómo llego a "la de próxima item "in a std :: list using iterator?" Esperaría que sea una lista enlazada, ¿por qué no puedo obtener el siguiente artículo?

+1

También tiene un error en su bucle 'for':' iterItems = dilList.end(); 'debe ser' iterItems! = DilList.end(); '. –

+0

@Fred - cierto. Gracias por señalar eso. –

+0

Posible duplicado de: http://stackoverflow.com/questions/3673684/peek-the-next-element-in-stl-container –

Respuesta

15

No puede hacer it + N porque no tiene acceso aleatorio para los iteradores de lista. Solo puede hacer un paso a la vez con iteradores de lista (estos son iteradores bidireccionales).

Puede utilizar boost::next y boost::prior

// Lookahead in list to see if next element is end 
if(boost::next(iterItems) == dilList.end()) 
{ 

O puede imprimir la coma antes:

std::string Compose(DataItemList& dilList) 
{ 
    std::stringstream ssDataSegment; 
    for(iterItems = dilList.begin(); 
     iterItems != dilList.end(); 
     ++iterItems) 
    { 
     if(iterItems != diList.begin()) 
      ssDataSegment << ","; 
     ssDataSegment << (*iterItems)->ToString(); 
    } 
    return ssDataSegment.str(); 
} 
+1

Nota: next() y prev() están en boost/utility.hpp. A menos que se muden a otro lugar. –

+0

La solución boost :: next() es elegante. Muchas gracias. –

+0

Técnicamente, es boost :: prior(). –

12

Creo que un iterador de lista es bidireccional, pero no de acceso aleatorio. Eso significa que puede hacer ++ y - a él, pero no agregar o restar.

Para obtener el siguiente iterador, haga una copia e increméntela.

2

podría evitar este problema por completo mediante el uso de:

std::string Compose(DataItemList& dilList) 
{ 
    std::stringstream ssDataSegment; 
    for(iterItems = dilList.begin(); iterItems != dilList.end(); iterItems++) 
    { 
     ssDataSegment << (*iterItems)->ToString() << ","; // always write "," 
    } 
    std::string result = ssDataSegment.str(); 
    return result.substr(0, result.length()-1); // skip the last "," 
} 

En primer lugar, escribe el "" para todos los elementos (incluso para el último). Luego, eliminas el último "no deseado", usando substr. Esto también da como resultado un código más claro.

+2

Siempre +1 a la solución de limpieza. ;-) – DevSolar

+2

No estoy de acuerdo con que este sea un código más claro. Nunca es más claro realizar operaciones extrañas y luego deshacerlas más tarde. Además, esto no es idiomático, ya que la forma idiomática es tratar la primera iteración como especial. -1 – rmeador

+0

Se trata de la mitad de la longitud de las otras soluciones y sigue en el mismo orden con respecto a la eficiencia.Yo, personalmente, considero ese "limpiador". Además, si hablamos de la ganancia neta de las operaciones, las OTRAS soluciones tienen una ganancia neta más alta. – Adam

6

Otra solución es tener la primera entrada sea el caso especial, en lugar de la última entrada:

std::string Compose(DataItemList& dilList) 
{ 
    std::stringstream ssDataSegment; 
    for(iterItems = dilList.begin(); 
     iterItems != dilList.end(); 
     ++iterItems) 
    { 
     // See if current element is the first 
     if(iterItems == dilList.begin()) 
     { 
      ssDataSegment << (*iterItems)->ToString(); 
     } 
     else 
     { 
      ssDataSegment << "," << (*iterItems)->ToString(); 
     } 
    } 
    return ssDataSegment.str(); 
} 
+0

El enfoque alternativo es asegurarse de que el contenedor no esté vacío, imprima el primer elemento, incremente el iterador, y el ciclo solo tiene su impresión "else" y no se necesita si es necesario. –

+0

@Mark B: Sí, vi que Johannes hizo exactamente eso (no vi eso en su respuesta cuando publiqué el mío). Eso es un poco más ordenado. –

1

Sin embargo, otra posibilidad:

#include "infix_iterator.h" 
#include <sstream> 

typedef std::list<shared_ptr<EventDataItem> > DataItemList; 

std::string Compose(DataItemList const &diList) { 
    std::ostringstream ret; 
    infix_ostream_iterator out(ret, ","); 

    for (item = diList.begin(); item != diList.end(); ++item) 
     *out++ = (*item)->ToString(); 
    return ret.str(); 
} 

Puede obtener infix_iterator.h de archivo Usenet de Google (o varios sitios web).

1

Nota: Desde C++ 11 puede usar std :: next y std :: prev.

Cuestiones relacionadas