2010-01-20 13 views
51

Ich finde boost::foreach sehr nützlich, da es mir eine Menge Schreiben erspart. Zum Beispiel, sagen wir, ich alle Elemente in einer Liste drucken möchten:Kann boost :: foreach mit std :: map verwendet werden?

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

boost :: foreach den Code oben viel simplier macht:

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

Viel besser! Jedoch habe ich nie einen Weg gefunden (wenn es überhaupt möglich ist), es für std::map s zu benutzen. Die Dokumentation enthält nur Beispiele mit Typen wie vector oder string.

+1

Das ist nicht gerade ein Duplikat ist, aber hier sehen: http://stackoverflow.com/questions/461507/how-to-use-boostforeach-with-a-boostptrmap/461908 # 461908 –

Antwort

88

Sie müssen verwenden:

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

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

Der Grund dafür ist, dass das Makro zwei Parameter erwartet. Wenn Sie versuchen, die Paardefinition zu verknüpfen, führen Sie ein zweites Komma ein, indem Sie dem Makro stattdessen drei Parameter zuweisen. Der Präprozessor berücksichtigt keine C++ - Konstrukte, er kennt nur Text. So

wenn Sie BOOST_FOREACH(pair<int, int>, map) sagen, sieht der Präprozessor diese drei Argumente für das Makro:

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

was falsch ist. Dies ist mentioned in der for-each-Dokumentation.

+0

Machen Sie das 'Paar '. – UncleBens

+1

Der letzte Schnitt führt zu einigen Fehlinformationen. Es gibt kein undefiniertes Verhalten, da die letzten beiden Beispiele nicht kompiliert werden. 'std :: map' schützt seinen Schlüssel selbst: Wenn Sie' map 'haben, dann ist der Werttyp' pair '. Beachten Sie, dass es den Schlüsseltyp const macht. – UncleBens

+0

Können Sie Ihre Antwort erneut bearbeiten? Ich habe versehentlich meinen upvote aufgehoben und jetzt wird es mich nicht umwandeln lassen, es sei denn du editierst = p EDIT: oh, cool, ich habe es selbst bearbeitet und es hat funktioniert :) +1 regiven! –

3

Sicher können Sie. Der Trick besteht jedoch darin, dass ein Map-Iterator auf ein Paar aus Schlüssel und Wert zeigt. Es würde wie folgt aussehen:

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

Nun, ich versuchte mit 'BOOST_FOREACH (int i, Karte)', 'BOOST_FOREACH (Paar , Karte)', etc. Kannst du a Arbeitsbeispiel? –

+3

Jemand könnte erwähnen, dass 'BOOST_FOREACH' ein * Makro * ist, und daher kann es nicht richtig mit dem Komma in der Paarvorlage umgehen. Dies ist der Grund, warum alle einen Typdef vorschlagen. – UncleBens

+0

@UncleBens: Ich denke, die Typedef macht es nur viel sauberer, auch wenn das Makro mit dem Komma umgehen kann (nicht sicher, ob es möglich ist). –

1

Ja:

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

MyMap myMap; 

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

Es ist möglich, aber es ist nicht wirklich der beste Weg, Dinge zu tun (wie ich ein paar Mal schon erwähnt habe, for_each so gut wie nie ist, und BOOST_FOREACH ist nur geringfügig besser). Für Ihr erstes Beispiel, ich glaube, Sie besser dran sein würden:

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

Es ziemlich ähnlich mit einer Karte arbeitet, mit der Ausnahme, dass Sie Betreiber < < für sie definieren müssen, da es nicht einen bereits definiert :

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

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

... und noch einmal, tut std::copy das ganz schön Job:

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

+1. Ich stimme Ihnen zu, Jerry, obwohl einige argumentieren könnten, dass die Definition des Operators (oops, Sie haben einen Tippfehler!) Mehr Probleme bereitet als der BOOST_FOREACH. –

+0

@Fred: Sie können argumentieren, und in einem extrem minimalen Ausmaß ist es sogar wahr. Andererseits ist es oft ein bisschen mehr Arbeit (zumindest im Vorfeld), die Arbeit richtig zu machen, als nur etwas zu hacken, das funktioniert. –

20

ich benutze Boost's Range Ex library die imp lenkt einige phantastische Bereichsadapter für das Iterieren über Kartenschlüssel oder Werte. Zum Beispiel:

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 Sie leichter tun können:

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

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

Laut Wikipedia ähnelt die foreach-Syntax mehr dem java foreach 'for (int & x: my_array) {x * = 2; } ' –

+0

Ich glaube, das Poster nahm die alte Praxis von' #include hannes

2

Typedefing eine Karte Paar verwirrend ist.Die einfachste Art, eine Karte iterieren ist mit einem Tupel (wie in Python):

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

Und keine Sorge, werden diese Kommata den Präprozessor nicht verwirren, weil ich Klammer um sie herum platziert.

+0

Dies hat einen Nachteil des Kopierens der Wert der Karte. Es könnte teuer sein, wenn es kein primitives ist. – balki

2

Ich mochte nicht die Idee, gezwungen zu werden, jedesmal typedefs hinzuzufügen, wenn ich eine foreach auf einer Karte benutzen wollte. So, hier ist meine Implementierung basiert auf dem Boost foreach Code:

#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)   

Dann können Sie es in Ihrem Code verwenden: #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"); 

Sie können auch die Werte ändern: #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

Wenn Sie typedefs nicht mögen, verwenden Sie einfach #define!? – portforwardpodcast

Verwandte Themen