Comment utiliser la boucle for () basée sur la plage avec std :: map?

336

L'exemple courant pour les boucles for () basées sur la plage C ++ 11 est toujours quelque chose de simple comme ceci:

std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7 };
for ( auto xyz : numbers )
{
     std::cout << xyz << std::endl;
}

Dans ce cas, xyzest un int. Mais que se passe-t-il lorsque nous avons quelque chose comme une carte? Quel est le type de la variable dans cet exemple:

std::map< foo, bar > testing = { /*...blah...*/ };
for ( auto abc : testing )
{
    std::cout << abc << std::endl;         // ? should this give a foo? a bar?
    std::cout << abc->first << std::endl;  // ? or is abc an iterator?
}

Lorsque le conteneur traversé est quelque chose de simple, il semble que les boucles for () basées sur une plage nous donneront chaque élément, pas un itérateur. Ce qui est bien ... s'il s'agissait d'un itérateur, la première chose que nous aurions toujours à faire est de le déférencer de toute façon.

Mais je ne sais pas à quoi m'attendre quand il s'agit de choses comme les cartes et les multimaps.

(Je suis toujours sur g ++ 4.4, alors que les boucles basées sur la plage sont dans g ++ 4.6+, donc je n'ai pas encore eu l'occasion de l'essayer.)

Stéphane
la source
4
La plage de déclaration fait une danse impie avec la bibliothèque standard std::beginet les std::endfonctions ou fonctions membres sous le même nom.
Gene Bushuyev
10
@will Sur un exemple de 3 lignes, vous vous faites prendre par le faux nom de variable?
Stéphane

Réponses:

495

Chaque élément du conteneur est un map<K, V>::value_type, qui est un typedefpour std::pair<const K, V>. Par conséquent, en C ++ 17 ou supérieur, vous pouvez écrire

for (auto& [key, value]: myMap) {
    std::cout << key << " has value " << value << std::endl;
}

ou comme

for (const auto& [key, value]: myMap) {
    std::cout << key << " has value " << value << std::endl;
}

si vous ne prévoyez pas de modifier les valeurs.

En C ++ 11 et C ++ 14, vous pouvez utiliser des forboucles améliorées pour extraire chaque paire par elle-même, puis extraire manuellement les clés et les valeurs:

for (const auto& kv : myMap) {
    std::cout << kv.first << " has value " << kv.second << std::endl;
}

Vous pouvez également envisager de marquer la kvvariable constsi vous souhaitez une vue en lecture seule des valeurs.

templatetypedef
la source
96

En C ++ 17, cela s'appelle des liaisons structurées , ce qui permet les opérations suivantes:

std::map< foo, bar > testing = { /*...blah...*/ };
for ( const auto& [ k, v ] : testing )
{
  std::cout << k << "=" << v << "\n";
}
dalle
la source
Est-il possible d'obtenir un const &à la clé, mais une référence non const à la valeur? (parce que c'est ce que fait map :: value_type ...)
peterchen
2
@peterchen: kest constsi vous utilisezfor(auto&[k,v]:testing)
Dalle
1
cpppreference sur les liaisons structurées en.cppreference.com/w/cpp/language/structured_binding
TankorSmash
Si vous compilez avec GCC, vous avez besoin de la version 7 ou supérieure pour les liaisons structurées: gcc.gnu.org/projects/cxx-status.html
csknk
25

De cet article: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2049.pdf

for( type-specifier-seq simple-declarator : expression ) statement

est syntaxiquement équivalent à

{
    typedef decltype(expression) C;
    auto&& rng(expression);
    for (auto begin(std::For<C>::begin(rng)), end(std::For<C>::end(rng)); begin != end; ++ begin) {
        type-specier-seq simple-declarator(*begin);
        statement
    }
}

Vous pouvez donc clairement voir que ce sera abcdans votre cas std::pair<key_type, value_type >. Donc, pour l'impression, vous pouvez accéder à chaque élément par abc.firstetabc.second

AK
la source
15

Si vous ne souhaitez voir que les clés / valeurs de votre carte et que vous utilisez boost, vous pouvez utiliser les adaptateurs boost avec les boucles basées sur la plage:

for (const auto& value : myMap | boost::adaptors::map_values)
{
    std::cout << value << std::endl;
}

il y a un boost équivalent :: adapters :: key_values

http://www.boost.org/doc/libs/1_51_0/libs/range/doc/html/range/reference/adaptors/reference/map_values.html

Pixie-Poop
la source
3

Si l'opérateur d'affectation de copie de foo et bar est bon marché (par exemple, int, char, pointer, etc.), vous pouvez effectuer les opérations suivantes:

foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)
{
  cout << "Foo is " << f << " Bar is " << b;
}
balki
la source
4
Le premier extrait de code n'utilise pas de "plage C ++ 11 basée sur ()". Ce n'est pas une réponse à "C ++ 11: comment utiliser la boucle for () basée sur une plage avec std :: map?"
isoiphone
1
@ytj Il est déjà mentionné dans la réponse que cela ne fonctionne pas. Je ne veux pas supprimer cela pour que les nouveaux utilisateurs n'aient pas à l'essayer et à le découvrir à nouveau.
balki