2010-01-20 14 views
51

Trovo che lo boost::foreach sia molto utile in quanto mi consente di risparmiare molte scritte. Per esempio, diciamo che voglio stampare tutti gli elementi in una lista:È possibile utilizzare 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 << " "; 

boost :: foreach rende il codice di cui sopra molto più semplice:

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

Molto meglio! Tuttavia non ho mai capito un modo (se è del tutto possibile) per usarlo per std::map s. La documentazione ha solo esempi con tipi come vector o string.

+1

Questo non è esattamente un duplicato, ma vedere qui: http://stackoverflow.com/questions/461507/how-to-use-boostforeach-with-a-boostptrmap/461908 # 461908 –

risposta

88

è necessario utilizzare:

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

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

Il motivo è che la macro si aspetta due parametri. Quando si tenta di allineare la definizione della coppia, si introduce una seconda virgola, rendendo invece i tre parametri della macro. Il preprocessore non rispetta alcun costrutto C++, conosce solo il testo.

Quindi, quando si dice BOOST_FOREACH(pair<int, int>, map), il preprocessore vede questi tre argomenti per la macro:

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

che è sbagliato. Questo è mentioned nella documentazione for-each.

+0

Crea quella coppia '. – UncleBens

+1

L'ultima modifica introduce una disinformazione. Non esiste un comportamento indefinito, poiché gli ultimi due esempi non verranno compilati. 'std :: map' protegge la sua chiave stessa: se hai' mappa 'il tipo di valore è' coppia '. Si noti che rende il tipo di chiave const. – UncleBens

+0

Puoi modificare di nuovo la tua risposta? Ho accidentalmente annullato la mia upvote e ora non mi farà rifondere se non modifichi = p EDIT: oh, bello, l'ho modificato io stesso e ha funzionato :) +1 regiven! –

3

Certo che puoi. Il trucco è, tuttavia, che un iteratore di mappe indica una coppia di chiave e valore. Sarebbe simile a questa:

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

Beh, ho provato con 'BOOST_FOREACH (int i, map)', 'BOOST_FOREACH (coppia , mappa)', ecc. Puoi pubblicare un esempio di lavoro? –

+3

Qualcuno potrebbe dire che "BOOST_FOREACH' è una * macro *, e quindi non può gestire correttamente la virgola nel modello di coppia. Questo è il motivo per cui tutti suggeriscono un typedef. – UncleBens

+0

@UncleBens: Penso che il typedef lo faccia sembrare molto più pulito, anche se la macro può gestire la virgola (non è sicuro se sia possibile). –

1

Sì:

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

MyMap myMap; 

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

E 'possibile, ma non è davvero il modo migliore per fare le cose (come ho già detto un paio di volte prima, for_each quasi mai a dire, e BOOST_FOREACH è solo marginalmente migliore). Per il vostro primo esempio, penso che sarebbe meglio con:

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

Funziona abbastanza simile con una mappa, se non che è necessario definire operatore < < per esso, dal momento che non ce n'è uno già definito :

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

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

... e ancora una volta, std::copy fa il lavoro abbastanza bene:

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

+1. Sono d'accordo con te, Jerry, anche se alcuni potrebbero obiettare che dover definire l'operatore (oops, hai un errore di battitura lì!) È più un problema del BOOST_FOREACH. –

+0

@Fred: possono argomentare che, e in misura estremamente minima, è persino vero. Poi di nuovo, fare il lavoro giusto spesso è un po 'più di lavoro (almeno in anticipo) piuttosto che hackerare qualcosa che funziona in quel modo. –

20

io uso Boost's Range Ex library che imp mostra alcuni adattatori di gamma di fantasia per iterare su chiavi o valori della mappa. Per esempio:

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

In C++ 0x è più facile fare:

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

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

Accoding to wikipedia la sintassi di foreach assomiglia più a java foreach 'for (int & x: my_array) {x * = 2; } ' –

+0

Credo che il poster abbia assunto la vecchia pratica di' #include hannes

2

Typedefing un paio mappa è confusa.Il modo più semplice per iterare una mappa è con una tupla (proprio come in python):

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

E non preoccupatevi, quelle virgole non si confondere il preprocessore perché ho messo tra parentesi intorno a loro.

+0

Questo ha uno svantaggio di copiare il valore della mappa. Potrebbe essere costoso se non è un primitivo. – balki

2

Non mi piaceva l'idea di dover aggiungere typedef ogni volta che volevo utilizzare un foreach su una mappa. Quindi ecco la mia implementazione basata sul codice spinta 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)   

Quindi è possibile utilizzare nel codice: #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"); 

È inoltre possibile modificare i valori: #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

se non ti piacciono i typedef, usa #define!? – portforwardpodcast

Problemi correlati