Use of for_each on map elements

151,353

Solution 1

You can iterate through a std::map object. Each iterator will point to a std::pair<const T,S> where T and S are the same types you specified on your map.

Here this would be:

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

If you still want to use std::for_each, pass a function that takes a std::pair<const int, MyClass>& as an argument instead.

Example:

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

And pass it to std::for_each:

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

Solution 2

C++11 allows you to do:

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

C++17 allows you to do:

for (const auto& [key, value] : myMap) {
    std::cout << key << " has value " << value << std::endl;
}

using structured binding.

UPDATE:

const auto is safer if you don't want to modify the map.

Solution 3

C++14 brings generic lambdas. Meaning we can use std::for_each very easily:

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;
});

I think std::for_each is sometimes better suited than a simple range based for loop. For example when you only want to loop through a subset of a map.

Solution 4

How about a plain C++? (example fixed according to the note by @Noah Roberts)

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

Solution 5

It's unfortunate that you don't have Boost however if your STL implementation has the extensions then you can compose mem_fun_ref and select2nd to create a single functor suitable for use with for_each. The code would look something like this:

#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>()));
}

Note that if you don't have access to compose1 (or the unary_compose template) and select2nd, they are fairly easy to write.

Share:
151,353
Antonio Pérez
Author by

Antonio Pérez

@apbarrero

Updated on September 03, 2020

Comments

  • Antonio Pérez
    Antonio Pérez over 3 years

    I have a map where I'd like to perform a call on every data type object member function. I yet know how to do this on any sequence but, is it possible to do it on an associative container?

    The closest answer I could find was this: Boost.Bind to access std::map elements in std::for_each. But I cannot use boost in my project so, is there an STL alternative that I'm missing to boost::bind?

    If not possible, I thought on creating a temporary sequence for pointers to the data objects and then, call for_each on it, something like this:

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

    It looks too obfuscated and I don't really like it. Any suggestions?