2011-08-06 16 views
256

El ejemplo común para C++ 11 gama de base para() bucles es siempre algo sencillo como este:Cómo usar el bucle for() basado en rangos con std :: map?

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

En cuyo caso xyz es un int. Pero, ¿qué sucede cuando tenemos algo así como un mapa? ¿Cuál es el tipo de la 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 recipiente se está atravesando es algo simple, parece que para() basada en bucles de gama nos dará cada elemento, no un iterador. Lo cual es bueno ... si fuera un iterador, lo primero que siempre tendríamos que hacer es eliminarlo de todos modos.

Pero estoy confundido en cuanto a lo que puede esperar cuando se trata de cosas como mapas y Multimapas.

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

+3

El rango para la declaración hace una danza impía con las funciones estándar de la biblioteca 'std :: begin' y' std :: end' o las funciones de miembros bajo el mismo nombre. –

+0

xyz es un horrible nombre de la variable en mi humilde opinión. Parece x multiplicado por y multiplicado por z o algo. O tres variables en uno, x, y y z que tampoco tienen sentido. ¿Por qué no solo usas x? – Will

+3

@will En un ejemplo de 3 líneas, ¿te encuentras atrapado en el nombre de la variable falsa? –

Respuesta

359

Cada elemento del contenedor es un map<K, V>::value_type , que es un typedef para std::pair<const K, V>. En consecuencia, se iba a escribir esto como

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

Para una mayor eficacia, es una buena idea para hacer que el parámetro en el bucle de referencia. También puede considerar hacerlo const si desea una vista de solo lectura de los valores.

+54

'auto & kv: myMap' sería mejor, para evitar copias innecesarias de cada valor en el' map <> '. – ildjarn

+55

'auto const & kv' hubiera sido mejor. +1 de todos modos. – Nawaz

+2

@Puppy, ¿puedes explicarlo? – Timmmm

24

De este trabajo: 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í se puede ver claramente que lo que es abc en su caso se std::pair<key_type, value_type >. Así que para la impresión de que puede hacer el acceso cada elemento por abc.first y abc.second

2

Si el operador de asignación de copia de foo y bar es barato (. Ej int, char, puntero, etc.), puede hacer lo siguiente:

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

EDITAR: El abajo no funciona como antes :, tiene que ser una declaración , no un lvalueexpresión.

foo f;bar b; 
for(std::tie(f,b) : testing) 
{ 
    cout << "Foo is " << f << " Bar is " << b; 
} 
+2

Debe haber una declaración, no una expresión, a la izquierda de ':'. – aschepler

+4

El primer fragmento de código no utiliza un "C++ 11 basado en rango para()". No es una respuesta a "C++ 11: cómo usar el bucle for() basado en rangos con std :: map?" – isoiphone

+0

voto abajo porque esto no funcionará. Según [n3853] (http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3853.htm), 'for (std :: tie (f, b): testing)' es realmente equivalente a 'for (auto && std :: tie (f, b): testing)', que está mal formado. – ytj

55

En C++ 17 esto se llama structured bindings, que permite la siguiente:

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

¿Es posible obtener un 'const &' para la clave, pero una referencia no const al valor? (porque eso es lo que hace map :: value_type ...) – peterchen

+1

@peterchen: 'k' es' const' si usa 'for (auto & [k, v]: testing)' – dalle

+0

cpppreferencia en enlaces estructurados http: // en .cppreference.com/w/cpp/language/structured_binding – TankorSmash