web-dev-qa-db-fra.com

Utilisation de for_each sur des éléments de carte

J'ai une carte où je voudrais effectuer un appel sur chaque fonction membre d'objet de type de données. Je sais pourtant comment faire cela sur n'importe quelle séquence, mais est-il possible de le faire sur un conteneur associatif?

La réponse la plus proche que j'ai pu trouver était la suivante: Boost.Bind pour accéder à std :: map elements dans std :: for_each . Mais je ne peux pas utiliser boost dans mon projet, existe-t-il une alternative STL qui me manque pour boost :: bind?

Si cela n’était pas possible, je pensais créer une séquence temporaire pour les pointeurs sur les objets de données, puis appeler for_each dessus, comme ceci:

class MyClass
{
public:
 void Method() const;
}

std::map<int, MyClass> Map;
//...

std::vector<MyClass*> Vector;
std::transform(Map.begin(), Map.end(), std::back_inserter(Vector), std::mem_fun_ref(&std::map<int, MyClass>::value_type::second));
std::for_each(Vector.begin(), Vector.end(), std::mem_fun(&MyClass::Method));

Ça a l'air trop obscur et je ne l'aime pas vraiment. Aucune suggestion?

37
Antonio Pérez

Vous pouvez parcourir un objet std::map. Chaque itérateur indiquera un std::pair<const T,S>T et S sont les mêmes types que ceux spécifiés sur votre map.

Ici ce serait:

for (std::map<int, MyClass>::iterator it = Map.begin(); it != Map.end(); ++it)
{
  it->second.Method();
}

Si vous souhaitez toujours utiliser std::for_each, transmettez une fonction qui prend un std::pair<const int, MyClass>& comme argument.

Exemple:

void CallMyMethod(std::pair<const int, MyClass>& pair) // could be a class static method as well
{
  pair.second.Method();
}

Et transmettez-le à std::for_each:

std::for_each(Map.begin(), Map.end(), CallMyMethod);
49
ereOn

C++ 11 vous permet de faire:

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

METTRE À JOUR:

const auto est plus sûr si vous ne voulez pas modifier la carte.

74
Sebastian

Que diriez-vous d'un simple C++? (exemple corrigé d'après la note de @Noah Roberts)

for(std::map<int, MyClass>::iterator itr = Map.begin(), itr_end = Map.end(); itr != itr_end; ++itr) {
  itr->second.Method();
}
8
bobah

C++ 14 apporte des lambdas génériques . Cela signifie que nous pouvons utiliser std :: for_each très facilement:

std::map<int, int> myMap{{1, 2}, {3, 4}, {5, 6}, {7, 8}};

std::for_each(myMap.begin(), myMap.end(), [](const auto &myMapPair) {
    std::cout << "first " << myMapPair.first << " second "
              << myMapPair.second << std::endl;
});

Je pense que std :: for_each est parfois mieux adapté qu'une simple plage basée sur une boucle. Par exemple, lorsque vous souhaitez uniquement parcourir un sous-ensemble d'une carte.

6
Christian Rapp

Il est regrettable que vous n’ayez pas Boost, mais si votre implémentation STL possède des extensions, vous pouvez composer mem_fun_ref et select2nd pour créer un seul foncteur utilisable avec for_each. Le code ressemblerait à ceci:

#include <algorithm>
#include <map>
#include <ext/functional>   // GNU-specific extension for functor classes missing from standard STL

using namespace __gnu_cxx;  // for compose1 and select2nd

class MyClass
{
public:
    void Method() const;
};

std::map<int, MyClass> Map;

int main(void)
{
    std::for_each(Map.begin(), Map.end(), compose1(std::mem_fun_ref(&MyClass::Method), select2nd<std::map<int, MyClass>::value_type>()));
}

Notez que si vous n'avez pas accès à compose1 (ou au modèle unary_compose) et à select2nd, ils sont assez faciles à écrire.

3
David Joyner

Pour les collègues programmeurs qui tombent sur cette question de Google, il existe un bon moyen d’utiliser boost.

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

Exemple concret pour votre commodité:

// typedef in include, given here for info : 
typedef std::map<std::string, std::string> Wt::WEnvironment::CookieMap

Wt::WEnvironment::CookieMap cookie_map = environment.cookies();

BOOST_FOREACH( const Wt::WEnvironment::CookieMap::value_type &cookie, cookie_map )
{
    std::cout << "cookie : " << cookie.first << " = " << cookie.second << endl;
}

prendre plaisir.

2
Offirmo

Voici un exemple d'utilisation de for_each pour une carte. 

std::map<int, int> map;

map.insert(std::pair<int, int>(1, 2));
map.insert(std::pair<int, int>(2, 4));
map.insert(std::pair<int, int>(3, 6));

auto f = [](std::pair<int,int> it) {std::cout << it.first + it.second << std::endl; };
std::for_each(map.begin(), map.end(), f);
0
HmdRmz

D'après mes souvenirs, la carte C++ peut vous retourner un itérateur de clés à l'aide de map.begin (). Vous pouvez utiliser cet itérateur pour parcourir toutes les clés jusqu'à atteindre map.end () et obtenir la valeur correspondante:. Carte C++

0
vodkhang

J'ai écrit ceci il y a quelque temps pour faire exactement ce que vous cherchez.

namespace STLHelpers
{
    //
    // iterator helper type for iterating through the *values* of key/value collections
    //

    /////////////////////////////////////////////
    template<typename _traits>
    struct _value_iterator
    {
        explicit _value_iterator(typename _traits::iterator_type _it)
            : it(_it)
        {
        }

        _value_iterator(const _value_iterator &_other)
            : it(_other.it)
        {
        }

        friend bool operator==(const _value_iterator &lhs, const _value_iterator &rhs)
        {
            return lhs.it == rhs.it;
        }

        friend bool operator!=(const _value_iterator &lhs, const _value_iterator &rhs)
        {
            return !(lhs == rhs);
        }

        _value_iterator &operator++()
        {
            ++it;
            return *this;
        }

        _value_iterator operator++(int)
        {
            _value_iterator t(*this);
            ++*this;
            return t;
        }

        typename _traits::value_type &operator->()
        {
            return **this;
        }

        typename _traits::value_type &operator*()
        {
            return it->second;
        }

        typename _traits::iterator_type it;
    };

    template<typename _tyMap>
    struct _map_iterator_traits
    {
        typedef typename _tyMap::iterator iterator_type;
        typedef typename _tyMap::mapped_type value_type;
    };

    template<typename _tyMap>
    struct _const_map_iterator_traits
    {
        typedef typename _tyMap::const_iterator iterator_type;
        typedef const typename _tyMap::mapped_type value_type;
    };
}
0
moswald

Ça va fonctionner pour vous ? 

class MyClass;
typedef std::pair<int,MyClass> MyPair;
class MyClass
{
  private:
  void foo() const{};
public:
static void Method(MyPair const& p) 
{
    //......
        p.second.foo();
};
}; 
// ...
std::map<int, MyClass> Map;
//.....
std::for_each(Map.begin(), Map.end(), (&MyClass::Method));
0
a1ex07

Juste un exemple:

template <class key, class value>
class insertIntoVec
{
public:
    insertIntoVec(std::vector<value>& vec_in):m_vec(vec_in)
    {}

    void operator () (const std::pair<key, value>& rhs)  
    {   
        m_vec.Push_back(rhs.second);
    }

private:
    std::vector<value>& m_vec;
};

int main()
{
std::map<int, std::string> aMap;
aMap[1] = "test1";
aMap[2] = "test2";
aMap[3] = "test3";
aMap[4] = "test4";

std::vector<std::string> aVec;

aVec.reserve(aMap.size());
std::for_each(aMap.begin(), aMap.end(),
          insertIntoVec<int, std::string>(aVec) 
    );

}

0
aJ.