Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over keys/values of a std::map in C++

My question is a slight generalization of this one. For sake of discussion, I'll focus on iterating over the keys of a map. I would like to have a generic helper function, key_iterator, that takes a map iterator and returns a map key iterator. For example, the following code:

#include "key_iterator.hpp"
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>

int main(int argc, char** argv)
{
    std::map<std::string, int> m;
    m["One"] = 1;
    m["Two"] = 2;
    std::copy(key_iterator(m.begin()), key_iterator(m.end()), std::ostream_iterator<std::string>(std::cout, " "));
    return 0;
}

should produce the following output:

One Two

As suggested in the solution to the question referred to above, boost::transform_iterator seems like an appropriate starting point for the implementation of key_iterator. I have a half-way solution that looks like this for key_iterator.hpp:

#pragma once

#include <functional>
#include <map>
#include <boost/iterator/transform_iterator.hpp>

template <typename Key, typename Value>
class KeyGetter : public std::unary_function<std::pair<Key,Value>, Key>
{
public:
    const Key& operator()(const std::pair<Key,Value>& p) const {return p.first;}
};

template<typename Key, typename Value>
boost::transform_iterator<KeyGetter<Key,Value>, typename std::map<Key,Value>::iterator>
key_iterator(typename std::map<Key,Value>::iterator itr)
{
    return boost::make_transform_iterator<KeyGetter<Key,Value>, typename std::map<Key,Value>::iterator>(itr, KeyGetter<Key,Value>());
}

but with this implementation the Key and Value types are not being deduced automatically and I need to supply them manually to get it to compile:

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

Any thoughts on how to get this working the way I want?

like image 201
dsmith Avatar asked Aug 17 '11 01:08

dsmith


1 Answers

Try this:

template <typename Iter>
struct KeyGetter : std::unary_function<typename Iter::value_type,
        typename Iter::value_type::first_type>
{
    const typename Iter::value_type::first_type& operator()
            (const typename Iter::value_type& p) const
        { return p.first; }
};

template<typename Iter>
boost::transform_iterator<KeyGetter<Iter>, Iter> key_iterator(Iter itr)
{
    return boost::make_transform_iterator<KeyGetter<Iter>, Iter>
        (itr, KeyGetter<Iter>());
}

The idea being that the function at the call site should be templated directly on its argument, to avoid having to specify the template arguments explicitly.

like image 147
John Zwinck Avatar answered Oct 13 '22 21:10

John Zwinck