web-dev-qa-db-fra.com

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

L'exemple commun des boucles for () basées sur la plage de C++ 11 est toujours simple:

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

Dans ce cas, xyz est un int. Mais que se passe-t-il quand 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 l'intervalle nous donneront chaque élément, pas un itérateur. Ce qui est bien ... si c'était un itérateur, la première chose à faire est de le déréférencer de toute façon.

Mais je ne sais pas trop à quoi s'attendre lorsqu'il s'agit d'éléments tels que des cartes et des cartes multiples.

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

308
Stéphane

Chaque élément du conteneur est un map<K, V>::value_type, qui est un typedef pour 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 boucles améliorées for pour extraire chaque paire séparément, puis extraire manuellement les clés et les valeurs:

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

Vous pouvez également envisager de marquer kv variable const si vous souhaitez une vue en lecture seule des valeurs.

442
templatetypedef

En C++ 17, cela s'appelle liaisons structurées , ce qui permet:

std::map< foo, bar > testing = { /*...blah...*/ };
for ( const auto& [ k, v ] : testing )
{
  std::cout << k << "=" << v << "\n";
}
91
dalle

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-specifier-seq simple-declarator(*begin);
        statement
    }
}

Ainsi, vous pouvez clairement voir que ce qui est abc dans votre cas sera std::pair<key_type, value_type >. Donc, pour imprimer, vous pouvez accéder à chaque élément par abc.first et abc.second

25
A. K.

Si vous souhaitez uniquement afficher les clés/valeurs de votre carte et si vous souhaitez utiliser un boost, vous pouvez utiliser les adaptateurs de 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

14
Pixie-Poop

Si l'opérateur d'attribution de copie de foo and bar est bon marché (par exemple, int, char, pointeur, etc.), vous pouvez procéder comme suit:

foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)
{
  cout << "Foo is " << f << " Bar is " << b;
}
2
balki