2010-01-20 33 views
51

Encuentro boost::foreach muy útil ya que me ahorra mucha escritura. Por ejemplo, digamos que quiero imprimir todos los elementos en una lista:¿Es posible usar boost :: foreach con std :: map?

std::list<int> numbers = { 1, 2, 3, 4 }; 
for (std::list<int>::iterator i = numbers.begin(); i != numbers.end(); ++i) 
    cout << *i << " "; 

impulso :: foreach hace que el código anterior mucho más simple:

std::list<int> numbers = { 1, 2, 3, 4 }; 
BOOST_FOREACH (int i, numbers) 
    cout << i << " "; 

Mucho mejor! Sin embargo, nunca encontré la forma (si es posible) de usarlo para std::map s. La documentación solo tiene ejemplos con tipos tales como vector o string.

+1

Esto no es exactamente un duplicado, pero ver aquí: http://stackoverflow.com/questions/461507/how-to-use-boostforeach-with-a-boostptrmap/461908 # 461908 –

Respuesta

88

Es necesario utilizar:

typedef std::map<int, int> map_type; 
map_type map = /* ... */; 

BOOST_FOREACH(const map_type::value_type& myPair, map) 
{ 
    // ... 
} 

La razón es que la macro espera dos parámetros. Cuando intenta alinear la definición de par, introduce una segunda coma, lo que hace que la macro tenga tres parámetros. El preprocesador no respeta ninguna construcción de C++, solo conoce el texto.

Así, cuando decimos BOOST_FOREACH(pair<int, int>, map), el preprocesador ve estos tres argumentos de la macro:

1. pair<int
2. int>
3. map

que está mal. Esto es mentioned en la documentación de cada uno.

+0

Haz que 'pair '. – UncleBens

+1

La última edición introduce información errónea. No hay un comportamiento indefinido, ya que los dos últimos ejemplos no se compilarán. 'std :: map' protege su propia clave: si tiene' map ', entonces el tipo de valor es' pair '. Tenga en cuenta que hace que el tipo de clave sea const. – UncleBens

+0

¿Puedes editar tu respuesta nuevamente? Accidentalmente rescindí mi voto popular y ahora no me hará refundirlo a menos que edites = p EDIT: oh, genial, lo edité yo mismo y funcionó :) +1 ¡regiven! –

3

Claro que puedes. El truco es, sin embargo, que un iterador de mapa apunta a un par de la clave y el valor. Se vería algo como esto:

typedef std::map<std::string, int> MapType; 
MapType myMap; 

// ... fill the map... 

BOOST_FOREACH(MapType::value_type val, myMap) 
{ 
    std::cout << val.first << ": " << val.second << std::endl; 
} 
+0

Bueno, probé con 'BOOST_FOREACH (int i, map)', 'BOOST_FOREACH (par , mapa)', etc. ¿Puedes publicar un ejemplo de trabajo? –

+3

Alguien podría mencionar que 'BOOST_FOREACH' es una * macro *, y por lo tanto no puede tratar adecuadamente con la coma en la plantilla de par. Esta es la razón por la cual todo el mundo está sugiriendo un typedef. – UncleBens

+0

@UncleBens: Creo que el typedef simplemente lo hace parecer mucho más limpio, incluso si el macro puede manejar la coma (no estoy seguro de si puede). –

1

Sí:

typedef std::map<std::string,int> MyMap; 

MyMap myMap; 

BOOST_FOREACH(MyMap::value_type loop, myMap) 
{ 
     // Stuff 
} 
2

Es posible, pero no es realmente la mejor manera de hacer las cosas (como he mencionado un par de veces antes, for_each casi nunca es, y BOOST_FOREACH es apenas un poco mejor). Para su primer ejemplo, creo que estaría mejor con:

std::copy(numbers.begin(), numbers.end(), 
      std::ostream_iterator<int>(std::cout, " ")); 

Funciona bastante similar con un mapa, excepto que usted tiene que definir el operador < < para ello, ya que no hay una ya definida :

typedef map<std::string, int>::value_type vt; 

std::ostream &operator<<(std::ostream &os, vt &v) { 
    return os << v.first << ": " << v.second; 
} 

... y una vez más, std::copy hace el trabajo bastante bien:

std::copy(mymap.begin(), mymap.end(), 
      std::ostream_iterator<vt>(std::cout, "\n")); 
+1

+1. Estoy de acuerdo contigo, Jerry, aunque algunos podrían argumentar que tener que definir al operador (¡Uy, tienes un error tipográfico ahí!) Es más problemático que BOOST_FOREACH. –

+0

@Fred: Pueden argumentar que, y en una medida extremadamente mínima, incluso es cierto. Por otra parte, hacer bien el trabajo a menudo requiere un poco más de trabajo (al menos por adelantado) que simplemente piratear algo que funciona. –

20

utilizo Boost's Range Ex library cual imp da acceso a algunos adaptadores de gama elegante para iterar sobre claves o valores del mapa. Por ejemplo:

map<int, string> foo; 
foo[3] = "three"; 
foo[7] = "seven"; 

BOOST_FOREACH(i, foo | map_keys) 
    cout << i << "\n"; 


BOOST_FOREACH(str, foo | map_values) 
    cout << str << "\n"; 
0

En C++ 0x se puede hacer más fácilmente:

map<int, string> entries; 
/* Fill entries */ 

foreach(auto i, entries) 
    cout << boost::format("%d = %s\n") % i.first % i.second; 
+1

Respondiendo a wikipedia la sintaxis de foreach se parece más a java foreach 'for (int & x: my_array) {x * = 2; } ' –

+0

Creo que el póster asumió la vieja práctica de' #include hannes

2

Typedefing un par mapa es confuso.La forma más sencilla de recorrer un mapa es con una tupla (al igual que en Python):

std::map<int, int> mymap; 
int key, value; 
BOOST_FOREACH(boost::tie(key, value), mymap) 
{ 
    ... 
} 

Y no se preocupe, esos comas no se confunda el preprocesador porque puse entre paréntesis alrededor de ellos.

+0

Esto tiene el inconveniente de copiar el valor del mapa. Puede ser costoso si no es un primitivo. – balki

2

No me gustó la idea de tener que agregar typedefs cada vez que quería utilizar un foreach en un mapa. Así que aquí está mi aplicación basado en el código de impulso foreach:

#ifndef MUNZEKONZA_FOREACH_IN_MAP 

#include <boost/preprocessor/cat.hpp> 
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x) BOOST_PP_CAT(x, __LINE__) 

namespace munzekonza { 
namespace foreach_in_map_private { 
inline bool set_false(bool& b) { 
    b = false; 
    return false; 
} 

} 
} 

#define MUNZEKONZA_FOREACH_IN_MAP(key, value, map)       \ 
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin();  \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();)  \ 
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true;  \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) &&    \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();   \ 
     (MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ?    \ 
     ((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) :   \ 
     (void)0)                \ 
    if(munzekonza::foreach_in_map_private::set_false(       \ 
      MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \ 
    for(key = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->first;   \ 
     !MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue);    \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)  \ 
    if(munzekonza::foreach_in_map_private::set_false(       \ 
      MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \ 
    for(value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second;  \ 
     !MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue);    \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)   

entonces usted puede utilizar en su código: #define foreach_in_map MUNZEKONZA_FOREACH_IN_MAP

std::map<int, std::string> mymap; 
mymap[0] = "oi"; 
mymap[1] = "noi"; 

std::map<int, std::string> newmap; 

foreach_in_map(int key, const std::string& value, mymap) { 
    newmap[key] = value; 
} 

ASSERT_EQ(newmap.size(), 2); 
ASSERT_EQ(newmap.count(0), 1); 
ASSERT_EQ(newmap.count(1), 1); 
ASSERT_EQ(newmap.at(0), "oi"); 
ASSERT_EQ(newmap.at(1), "noi"); 

También puede cambiar los valores: #define foreach_in_map MUNZEKONZA_FOREACH_IN_MAP

std::map<int, std::string> mymap; 

mymap[0] = "oi"; 
mymap[1] = "noi"; 

std::map<int, std::string> newmap; 

foreach_in_map(int key, std::string& value, mymap) { 
    value = "voronoi" + boost::lexical_cast<std::string>(key); 
} 

ASSERT_EQ(mymap.size(), 2); 
ASSERT_EQ(mymap.count(0), 1); 
ASSERT_EQ(mymap.count(1), 1); 
ASSERT_EQ(mymap.at(0), "voronoi0"); 
ASSERT_EQ(mymap.at(1), "voronoi1"); 
+2

si no te gustan los typedefs, solo usa #define!? – portforwardpodcast

Cuestiones relacionadas