140 votes

Ce qui ' s le meilleur moyen de parcourir de deux ou de plusieurs récipients en même temps

C ++11 offre plusieurs moyens pour itérer sur les conteneurs. Par exemple :

Basé sur une plage de boucle

std::for_each

Cependant, ce qui est la méthode recommandée pour itérer sur deux (ou plusieurs) récipients de même taille pour accomplir quelque chose comme :

71voto

Konrad Rudolph Points 231505

Plutôt en retard à la fête. Mais: je voudrais effectuer une itération sur les indices. Mais pas avec le classique de la for boucle, mais plutôt avec une gamme à base for boucle sur les indices:

for(unsigned i : indices(containerA))
    containerA[i] = containerB[i];

indices est un simple wrapper fonction qui retourne un (paresseusement évalué) gamme pour les indices. Depuis la mise en œuvre – bien que simple, est un peu trop long pour le poster ici, vous pouvez trouver une application sur GitHub.

Ce code est aussi efficace que l'utilisation d'un manuel classique, for boucle.

Si cette situation se produit souvent dans vos données, pensez à utiliser un autre modèle qui zips deux séquences et produit une gamme de tuples, correspondant à des éléments appariés:

for (auto items&& : zip(containerA, containerB))
    get<0>(items) = get<1>(items);

La mise en œuvre de l' zip est laissé comme exercice pour le lecteur, mais il suit facilement à partir de la mise en œuvre de l' indices.

40voto

Xeo Points 69818

Pour votre exemple, il suffit d'utiliser

std::copy_n(contB.begin(), contA.size(), contA.begin())

Pour le cas plus général, vous pouvez utiliser Boost.Itérateur de l' zip_iterator, avec une petite fonction pour le rendre utilisable dans la gamme à base de boucles. Pour la plupart des cas, cela va fonctionner:

template<class... Conts>
auto zip_range(Conts&... conts)
  -> decltype(boost::make_iterator_range(
  boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
  boost::make_zip_iterator(boost::make_tuple(conts.end()...))))
{
  return {boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
          boost::make_zip_iterator(boost::make_tuple(conts.end()...))};
}

// ...
for(auto&& t : zip_range(contA, contB))
  std::cout << t.get<0>() << " : " << t.get<1>() << "\n";

Exemple vivant.

Cependant, pour de plein fouet l'genericity, vous voulez probablement quelque chose de plus comme cequi fonctionne correctement pour les tableaux et les types définis par l'utilisateur qui n'ont pas de membre de l' begin()/end() mais n' ont begin/end fonctions dans leur espace de noms. Aussi, cela permet à l'utilisateur d'obtenir plus précisément const d'accès par le biais de l' zip_c... fonctions.

Et si vous êtes un défenseur de nice, messages d'erreur, comme moi, alors vous voudrez probablement cequi vérifie si les récipients provisoires ont été adoptées à l'une de la zip_... fonctions, et affiche un joli message d'erreur le cas échéant.

9voto

wjl Points 2904

Il y a beaucoup de façons de faire des choses spécifiques avec plusieurs conteneurs prévus à l' algorithm - tête. Par exemple, dans l'exemple que vous avez donné, vous pouvez utiliser std::copy plutôt d'une volonté explicite de la boucle.

D'autre part, il n'est pas intégré de manière générique itérer plusieurs récipients autres que la normale pour la boucle. Ce n'est pas surprenant, car il y a beaucoup de façons pour effectuer une itération. Pensez-y: vous pouvez parcourir un conteneur avec une étape, un conteneur avec une autre étape; ou par l'intermédiaire d'un récipient jusqu'à ce qu'il arrive à la fin et puis commencer à insérer tandis que vous allez jusqu'à la fin de l'autre contenant; une étape de la première conteneur pour chaque fois que vous avez complètement passer par l'autre récipient puis recommencer; ou de quelque autre motif, ou de plus de deux conteneurs à la fois; etc ...

Toutefois, si vous voulez faire votre propre "for_each" style de la fonction qui parcourt les deux récipients seulement jusqu'à la longueur de la plus courte, vous pourriez faire quelque chose comme ceci:

template <typename Container1, typename Container2>
void custom_for_each(
  Container1 &c1,
  Container2 &c2,
  std::function<void(Container1::iterator &it1, Container2::iterator &it2)> f)
{
  Container1::iterator begin1 = c1.begin();
  Container2::iterator begin2 = c2.begin();
  Container1::iterator end1 = c1.end();
  Container2::iterator end2 = c2.end();
  Container1::iterator i1;
  Container1::iterator i2;
  for (i1 = begin1, i2 = begin2; (i1 != end1) && (i2 != end2); ++it1, ++i2) {
    f(i1, i2);
  }
}

Évidemment, vous pouvez faire toute sorte d'itérations de la stratégie que vous voulez d'une manière similaire.

Bien sûr, vous pourriez dire que juste faire la boucle intérieure est plus facile que d'écrire une fonction personnalisée comme ça ... et vous auriez raison, si vous allez seulement faire une ou deux fois. Mais la bonne chose est que c'est très réutilisables. =)

8voto

czarles Points 11

Lorsque vous avez besoin effectuer une itération simultanément plus de 2 conteneurs seulement, s’il y a une version étendue de l’algorithme for_each standard dans la bibliothèque de boost de gamme, par exemple :

Lorsque vous avez besoin gérer plus de 2 conteneurs dans un algorithme, puis vous devrez jouer avec fermeture à glissière.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X