5

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?

Community
  • 1
  • 1
dsmith
  • 304
  • 4
  • 12
  • Similar question: http://stackoverflow.com/questions/110157/how-to-retrieve-all-keys-or-values-from-a-stdmap/110228#110228 – Nicolae Dascalu Aug 17 '11 at 02:17
  • What's wrong with `map_keys` from boost.range? http://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/adaptors/reference/map_keys.html – Cubbi Aug 17 '11 at 02:24
  • @dnikku - Thanks there are some good suggestions there, but none that quite do what I'd like key_iterator to do, namely, return a map "key iterator" given a map "iterator". – dsmith Aug 17 '11 at 02:30
  • @Cubbi - Thanks! I wasn't aware of map_keys. I am still interested in seeing how to modify my function template to provide automatic deduction of the template args, though. – dsmith Aug 17 '11 at 02:42

1 Answers1

7

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.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436