2010-04-23 5 views
9

J'essaye de construire une chaîne en utilisant des éléments de données stockés dans une liste std ::, où je veux placer des virgules seulement entre les éléments (ie, si les éléments sont {A, B , C, D} dans la liste, la chaîne de résultats doivent être "A, B, C, D"std :: list itérateur: get next element

Ce code ne fonctionne pas.

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

Comment puis-je obtenir à « la Continuer- item "dans une liste std :: en utilisant un itérateur? Je m'attendrais à ce que ce soit une liste liée, pourquoi ne puis-je pas passer à l'article suivant?

+1

Vous avez également une erreur dans votre boucle 'for':' iterItems = dilList.end(); 'devrait être' iterItems! = DilList.end(); '. –

+0

@Fred - Vrai. Merci d'avoir fait remarquer cela. –

+0

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

Répondre

15

Vous ne pouvez pas faire it + N car vous n'avez pas d'accès aléatoire pour les itérateurs de liste. Vous ne pouvez faire qu'une seule étape à la fois avec les itérateurs de liste (ce sont des itérateurs bidirectionnels).

Vous pouvez utiliser boost::next et boost::prior

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

Ou vous pouvez imprimer la virgule avant:

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

Remarque: next() et prev() sont dans boost/utility.hpp. À moins qu'ils déménagent ailleurs. –

+0

La solution boost :: next() est élégante. Merci beaucoup. –

+0

Techniquement, c'est boost :: prior(). –

12

Je crois qu'une iterator liste est bidirectionnelle, mais pas un accès aléatoire. Cela signifie que vous pouvez faire ++ et - mais pas ajouter ou soustraire.

Pour obtenir l'itérateur suivant, faites une copie et incrémentez-la.

2

Vous pouvez éviter ce problème tout à fait à l'aide:

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 "," 
} 

Vous écrivez d'abord le « » pour tous les éléments (même pour le dernier). Ensuite, vous supprimez le dernier "," en utilisant substr. Cela entraîne en outre un code plus clair.

+2

Toujours +1 à la solution nettoyante. ;-) – DevSolar

+2

Je ne suis pas d'accord que c'est un code plus clair. Il n'est jamais plus clair de faire des opérations superflues et ensuite de les annuler plus tard. En outre, ceci est non-idiomatique, vu que la manière idiomatique est de traiter la première itération comme spéciale. -1 – rmeador

+0

C'est environ la moitié de la longueur des autres solutions et c'est toujours dans le même ordre d'efficacité.Personnellement, je considère que "nettoyant". En outre, si nous parlons du gain net d'exploitation, les solutions AUTRES ont un gain net plus élevé. – Adam

6

Une autre solution est d'avoir la première entrée soit le cas particulier, au lieu de la dernière entrée:

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

L'approche alternative consiste à s'assurer que le conteneur n'est pas vide, à imprimer le premier élément, à incrémenter l'itérateur et à ce que la boucle ait simplement votre impression "else" et non si/sinon nécessaire. –

+0

@Mark B: Oui, j'ai vu Johannes faire exactement cela (je ne l'ai pas vu dans sa réponse quand j'ai posté le mien). C'est un peu plus net. –

1

Encore une autre possibilité:

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

Vous pouvez obtenir infix_iterator.h de l'archive Usenet de Google (ou divers sites Web).

1

Remarque: Depuis C++ 11, vous pouvez utiliser std :: suivant et std :: prev.

Questions connexes