52 votes

Est-il possible d'utiliser boost :: foreach avec std :: map?

Je trouve boost::foreach très utile car il me permet de gagner beaucoup de l'écriture. Par exemple, disons que je veux imprimer tous les éléments dans une liste:

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

boost::foreach rend le code ci-dessus beaucoup plus simple:

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

Beaucoup mieux! Cependant je n'ai jamais trouvé un moyen (si c'est possible) à utiliser pour std::maps. La documentation a des exemples avec des types comme vector ou string.

88voto

GManNickG Points 155079

Vous devez utiliser:

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

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

La raison en est que la macro s'attend deux paramètres. Lorsque vous essayez d'insérer le paire de définition, d'introduire un deuxième virgule, faire de la macro trois paramètres à la place. Le préprocesseur ne respecte pas toutes les constructions C++, il ne connaît que le texte.

Donc quand vous dites BOOST_FOREACH(pair<int, int>, map), le préprocesseur voit ces trois arguments de la macro:

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

Ce qui est faux. C'est mentionné dans la for-each de la documentation.

20voto

Manuel Points 5396

J’ai utiliser gamme Ex bibliothèque Boost qui implémente certains adaptateurs gamme fantaisie de parcourir les valeurs ou les clés de la carte. Par exemple :

3voto

Fred Larson Points 27404

Bien sûr vous pouvez. La tour est, cependant, qu’un itérateur carte pointe sur une paire de la clé et la valeur. Il devrait ressembler à ceci :

2voto

Jerry Coffin Points 237758

C'est possible, mais ce n'est pas vraiment la meilleure façon de faire les choses (comme je l'ai mentionné à quelques reprises avant, for_each presque jamais, et BOOST_FOREACH est à peine mieux). Pour votre premier exemple, je pense que vous seriez mieux avec:

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

Il fonctionne assez similaire avec une carte, sauf que vous devez définir l'opérateur<< pour elle, car il n'est pas déjà défini:

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

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

...et une fois de plus, en std::copy fait le travail très bien:

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

2voto

marko.ristin Points 28

Je n’aimais pas l’idée d’être obligé d’ajouter du texte chaque fois que je voulais utiliser un foreach sur une carte. Voici donc mon implémentation basée sur le code boost 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)        
 

Ensuite, vous pouvez l’utiliser dans votre code: #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" );
 

Vous pouvez également modifier les valeurs: #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" );
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X