¿Cómo utilizar el bucle for() basado en rango con std::map?

Resuelto Stéphane asked hace 13 años • 5 respuestas

El ejemplo común para bucles for() basados ​​en rangos de C++11 es siempre algo simple como esto:

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

En cuyo caso xyzes un int. Pero, ¿qué pasa cuando tenemos algo así como un mapa? ¿Cuál es el tipo de variable en este ejemplo?

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?
}

Cuando el contenedor que se atraviesa es algo simple, parece que los bucles for() basados ​​en rangos nos darán cada elemento, no un iterador. Lo cual es bueno... si fuera un iterador, lo primero que siempre tendríamos que hacer es desreferenciarlo de todos modos.

Pero no sé qué esperar cuando se trata de cosas como mapas y mapas múltiples.

(Todavía estoy en g++ 4.4, mientras que los bucles basados ​​en rangos están en g++ 4.6+, así que no he tenido la oportunidad de probarlo todavía).

Stéphane avatar Aug 06 '11 07:08 Stéphane
Aceptado

Cada elemento del contenedor es un map<K, V>::value_type, que es un typedeffor std::pair<const K, V>. En consecuencia, en C++ 17 o superior, puedes escribir

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

o como

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

si no planea modificar los valores.

En C++11 y C++14, puede usar forbucles mejorados para extraer cada par por sí solo y luego extraer manualmente las claves y los valores:

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

También podría considerar marcar la kvvariable constsi desea una vista de solo lectura de los valores.

templatetypedef avatar Aug 06 '2011 00:08 templatetypedef

En C++17 esto se denomina enlaces estructurados , que permiten lo siguiente:

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

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

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

es sintácticamente equivalente a

{
    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
    }
}

Así podrás ver claramente que abcserá lo que sucede en tu caso std::pair<key_type, value_type >. Entonces, para imprimir, puede acceder a cada elemento mediante abc.firstyabc.second

A. K. avatar Aug 06 '2011 00:08 A. K.

Si el operador de asignación de copia de foo y bar es económico (por ejemplo, int, char, pointer, etc.), puedes hacer lo siguiente:

foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)
{
  cout << "Foo is " << f << " Bar is " << b;
}
balki avatar Jan 22 '2013 18:01 balki