views:

403

answers:

6

Is there any way to rename the first and second accessor functions of a map iterator. I understand they have these names because of the underlying pair which represents the key and value, but I'd like the iterators to be a little more readable. I think this might be possible using an iterator adaptor, but I'm not sure how to implement it.

Please note that I can't use boost.

Example of what I mean:

map<Vertex, Edge> adjacency_list;
for(map<Vertex, Edge>::iterator it = adjacency_list.begin();
    it != adjacency_list.end();
    ++it)
{
    Vertex v = it->first;
    //instead I would like to have it->vertex
}
+3  A: 

Sure, reimplement or wrap iterator, but is it worth the effort? Wouldn't

Vertex& v = it->first;

be easier?

Michael Krelin - hacker
Also remember that using first and second of the std::map iterator is such a common idiom that you'd confuse anyone reading your code - a bit.
phoku
That true, but from the sanity perspective, `first` and `second` are confusing enough ;-)
Michael Krelin - hacker
+4  A: 

Unfortunately, no. What I usually do is this:

typedef map<Vertex, Edge> AdjacencyList;
typedef AdjacencyList::value_type Vertex_Edge_Pair;

For readability. Inside your loop you can also say

Vertex& current_vertex = it->first;
Edge& current_edge = it->second;
rlbond
+6  A: 

If you're just concerned about readability you could do something like this:

typedef map<Vertex, Edge> AdjacencyList;
struct adjacency
{
    adjacency(AdjacencyList::iterator& it) 
      : vertex(it->first), edge(it->second) {}
    Vertex& vertex;
    Edge& edge;
};

And then:

Vertex v = adjacency(it).vertex;
Georg Fritzsche
+2  A: 

You can't rename the members, but you can have some functions to help.

inline Vertex& vertex(map<Vertex, Edge>::iterator& it) {return it->first;}
inline Edge& edge(map<Vertex, Edge>::iterator& it) {return it->second;}

Then, instead of it->vertex like you want, you can do vertex(it)

KeithB
+1 Also add overloads for const_iterators.
UncleBens
+1  A: 

I wouldn't recommend really using this, but it does seem to work, at least to the minimum degree of the test program doing what I wanted/expected:

#include <map>
#include <string>
#include <iostream>

template <class T, class U>
struct my_pair : public std::pair<T, U> {
    T const &vertex;
    my_pair(std::pair<T, U> const &x) : std::pair<T, U>(x), vertex(x.first) { }
};

template <class T, class U>
struct my_map : public std::map<T, U> { 
    my_pair<T, U> find(T const &t) { return my_pair<T, U>(*std::map<T,U>::find(t)); }
};

class Vertex { 
    int x;
public:
    Vertex(int v) : x(v) {}
    bool operator<(Vertex const &other) const { return x < other.x; }
    friend std::ostream &operator<<(std::ostream &os, Vertex const &v) { return os << v.x; }
};

int main() { 
    my_map<Vertex, std::string> m;

    m[1] = "This is it";

    my_pair<Vertex, std::string> mp = m.find(1);
    std::cout << mp.vertex << ": " << mp.second;
    return 0;
}
Jerry Coffin
A: 

I liked KeithB's solution with free functions. However, a more reusable solution might be nice.

What about function objects that access first or second, as you can name the instances anything you like:

#include <map>
#include <string>
#include <iostream>

struct GetFirst
{
    template <class First, class Second>
    First& operator()(std::pair<First, Second>& p)
    {
        return p.first;
    }

    template <class First, class Second>
    const First& operator()(const std::pair<First, Second>& p)
    {
        return p.first;
    }
};

struct GetSecond
{
    template <class First, class Second>
    Second& operator()(std::pair<First, Second>& p)
    {
        return p.second;
    }

    template <class First, class Second>
    const Second& operator()(const std::pair<First, Second>& p)
    {
        return p.second;
    }
};

int main()
{
    typedef std::map<std::string, int> Map;

    Map persons;
    persons["John"] = 20;
    persons["Mary"] = 24;

    //create named accessors
    GetFirst name;
    GetSecond age;

    for (Map::iterator it = persons.begin(); it != persons.end(); ++it) {
        std::cout << name(*it) << " is aging.\n";
        ++age(*it);
    }

    for (Map::const_iterator it = persons.begin(); it != persons.end(); ++it) {
        std::cout << "Name: " << name(*it) << ", age: " << age(*it) << '\n';
    }
}

This is the best I could do. I also tried to make those functors accept the iterator directly, but one way or another this means that the signature will contain dependent names which apparently makes template type deduction impossible (I couldn't find a way to overload GetSecond for iterator/const_iterator even with deferred return type of C++0x).

UncleBens