7
votes

Comment faire fonctionner le conteneur d'argument indépendant

J'écris une fonction d'utilité qui prendra un vecteur d'éléments (pourrait être une chaîne, int, une double, un char) et concaténer dans une seule chaîne et le renvoyer. Il ressemble à ceci: xxx

Je voudrais faire cette fonction plus générique:

  • Utilisez d'abord les itérateurs au lieu d'utiliser des indices pour le vecteur . J'ai essayé ça std :: vecteur :: const_itétorator it = vec.begin () Avant la boucle et le compilateur m'a donné une erreur: : Erreur: attendu ; avant Quand je change les définitions ci-dessus en std :: vecteur :: const_itérator it = vec.begin () L'erreur disparaît. Donc, on dirait que je ne suis pas suivant la syntaxe correcte, s'il vous plaît laissez-moi savoir ce qu'il est
  • La seconde est de rendre la fonction plus générique en faisant le premier conteneur d'argument indépendant. Compte tenu de tout conteneur ( vecteur , list , , , , deque , etc.) je veux faire la même chose comme ci-dessus. J'ai essayé de chercher cela à Stackoverflow et n'a pas trouvé de réponse satisfaisante.

0 commentaires

5 Réponses :


6
votes

Étape 1, comme vous l'avez dit, utilisez des itérateurs:

template<typename container>
std::string convert2Str(container const& vec)
{
   using std::begin;
   using std::end;
   std::ostringstream sStream;
   for (auto it = begin(vec); it != end(vec); ++it) {
      typedef decltype(*it) T; // if needed
      sStream << *it << " "; 
   }
   return sStream.str(); 
}


6 commentaires

Quelle est la différence entre son point 1 et le vôtre. Utiliser STD :: Vecteur :: Const_ITHERATORATORATORATORATORATORATORATOR, mais tapez defer cela fait? Je suis désorienté. Sa solution manque simplement à un typename devant la déclaration d'itérateur. (Comme le compilateur déclare) mais pourquoi? On se sent comme une structure manquante en C devant un type non typédecide.


@Ronny: C'est le mot-clé typename qui y est nécessaire. Le typeDEF est juste utilisé pour faciliter la transition vers un conteneur générique ( t et est toujours défini, mais je chante lequel est l'argument de modèle).


Je suppose que la ligne "Typef conteneur :: value_type t; // si nécessaire" est ce qui indique au compilateur que le conteneur est un vecteur, une liste, etc. et garantit que Convert2Str ne peut pas être appelé avec un simple type de données tel que INT ou DOUBLE. Alors, pourquoi cette déclaration est-elle marquée "si nécessaire" .. Merci pour des réponses rapides.


@srikrish: cette ligne ne vous rappelle que le type t qui existait dans votre code d'origine. Cette fonction n'a pas utilisé t n'importe où, mais je voulais vous montrer comment y accéder au cas où. Même sans cette ligne, si vous essayiez de passer quelque chose qui n'était pas un conteneur, le compilateur se plaint des appels à commencez et fin .


Donc, si T est un type défini par l'utilisateur (ma propre classe), puis-je faire quelque chose comme ce sstream << t.gevalue () << ""; ??


@Srikrish: Non, vous ne pouvez pas utiliser . après un type. Mais vous voudrez peut-être déclarer une variable locale de type t , peut-être quelque chose comme t somme = * it + 5; .



0
votes

Je pense que cela devrait fonctionner: xxx

démo: http://ideone.com / 9puvv


0 commentaires

2
votes

Utilisez ceci. Vous avez besoin du typosame code> pour dire au compilateur qu'il devrait envisager T :: const_itéator code> un type lors de l'analyse, il ne peut pas vraiment savoir que cela est vrai jusqu'à ce que vous Appelez effectivement la fonction qui passe quelque peu t code> qui comporte un type const_iterator code> type.

template<typename T>
std::string convert2Str(T const& cont) 
{
    std::ostringstream sStream; 
    for (typename T::const_iterator it = cont.begin(); it != cont.end(); ++it) {
        sStream << *it << " "; 
    }
    return sStream.str(); 
}


1 commentaires

Bon point sur typename . Juste une autre raison de passer à C ++ 0x dès que possible.



3
votes

C'est plus facile si vous vous clarifiez uniquement sur le type de conteneur. Le type de valeur est stocké dans tous les conteneurs standard, boost et qt en tant que membre Typedef code> MEMBRE VALUE_TYPE CODE>. std :: copier code> et ostream_itéator code> vous permet de sauter les dernières déclarations d'itérateur.

template <typename Container>
std::string convert2Str(Container const &cont)
{
    std::ostringstream s;
    std::copy(cont.begin(), cont.end(),
              std::ostream_iterator<typename Container::value_type>(s, " "));
    return s.str();
}


5 commentaires

Bien sûr, cela n'enseigne pas à Srikrish comment écrire une fonction générique de la sienne, ce qui est également une compétence utile. Et ostream_iterator sera meilleur avec C ++ 0x.


@BEN: déclinger est déroutant; On dirait que vous courez / évaluer une expression, alors que vous n'êtes pas vraiment. Tout conteneur compatible STL aura valeur_type (les conteneurs standard font, les conteneurs de boost font et même ceux de QT).


@Larsman: Désolé, il devrait vraiment être ostream_iterator . Les tableaux bruts n'ont pas valeur_type , mais ils fonctionnent avec std :: commencent et std :: end . Et il devrait définitivement être en utilisant std :: copie; Copier (...); pour laisser la recherche de koenig faire sa chose. N'oubliez pas que les modèles de fonctions ne peuvent pas être partiellement spécialisés et définir de nouvelles surcharges dans Nomspace std est interdit, le seul moyen de fournir une version optimisée de copier pour un conteneur utilise ADL.


* Ce code est-il portable aux plates-formes telles que Solaris? J'ai récemment utilisé STD :: compter dans mon code et le compilateur Sun à Solaris se plaignit de cela. Pouvez-vous expliquer ce que fait cette déclaration "std :: ostream_iterator (s," ")" DO? Merci


@srikrish: cela fonctionne sur tout compilateur C ++ conformément à la norme, mais je ne sais pas si le compilateur Sun se conformait. Voir Cplusplus.com/reference/std/iterator/ostream_iterator .



5
votes

Après la pratique STL, je vous recommanderais d'utiliser deux itérateurs pour les paramètres d'entrée, au lieu d'un conteneur (pour un motif évident de pouvoir fonctionner avec seulement une partie d'un conteneur, et généralement avec une séquence définie par les itérateurs):

std::vector<int> int_vec;
std::list<float> f_list;
std::deque<std::string> str_deq;

     // put something into the containers here

std::cout<< convert2Str(int_vec.begin(), int_vec.end()) <<std::endl;
std::cout<< convert2Str(f_list.begin(), f_list.end()) <<std::endl;
std::cout<< convert2Str(str_deq.begin(), str_deq.end()) <<std::endl;


0 commentaires