2009-09-30 14 views
16

Czy istnieje sposób na zmianę nazwy pierwszej i drugiej funkcji akcesora iteratora mapy. Rozumiem, że mają te nazwy ze względu na podstawową parę, która reprezentuje klucz i wartość, ale chciałbym, aby iteratory były trochę bardziej czytelne. Myślę, że to może być możliwe za pomocą adaptera iteratora, ale nie jestem pewien, jak go zaimplementować.Zmiana nazwy pierwszego i drugiego iteratora mapy

Należy pamiętać, że nie mogę użyć doładowania.

Przykład tego, co mam na myśli:

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 
} 
+1

Zabawne: Obecnie pojawia się wiele pytań związanych z wykresem: http://stackoverflow.com/questions/1499878/use-a-graph-library-node-network-library-or-roll-my- własny, http://stackoverflow.com/questions/1499217/boost-graph-as-basis-for-a-simple-dag-graph –

+0

Dlaczego nie możesz użyć boost? –

Odpowiedz

15

Jeśli jesteś po prostu zaniepokojony czytelności mógłby zrobić coś takiego:

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

, a następnie:

Vertex v = adjacency(it).vertex; 
5

Jasne, reimplement lub owinąć iterator, ale jest warte wysiłku? Czy nie byłby łatwiejszy w użyciu?

+3

Należy również pamiętać, że używanie pierwszego i drugiego iteratora std :: map jest tak powszechnym idiomem, że można by pomylić każdego czytającego twój kod - trochę. – phoku

+3

To prawda, ale z punktu widzenia zdrowia psychicznego, "pierwszy" i "drugi" są wystarczająco mylące ;-) –

7

Niestety, nie. Zazwyczaj robię to:

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

Dla czytelności. Wewnątrz pętli można również powiedzieć

Vertex& current_vertex = it->first; 
Edge& current_edge = it->second; 
12

Nie możesz zmień nazwy członków, ale możesz mieć kilka funkcji, które Ci pomogą.

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

Następnie zamiast it->vertex jak chcesz, możesz zrobić vertex(it)

+1

+1 Dodaj także przeciążenia dla const_iterators. – UncleBens

1

Nie polecam naprawdę za pomocą tego, ale to nie wydają się działać, przynajmniej w minimalnym stopniu programu testowego robienie tego, co chciałem/oczekiwałem:

#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; 
} 
1

Podobało mi się rozwiązanie KeithB z darmowymi funkcjami. Jednak lepsze może być rozwiązanie wielokrotnego użytku.

Co o obiektach funkcyjnych, które mają dostęp pierwsze lub drugie, jak można nazwać coś instancji lubisz:

#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'; 
    } 
} 

To najlepsze co mogłem zrobić. Próbowałem również, aby te funktory zaakceptować iterator bezpośrednio, ale tak czy inaczej to oznacza, że ​​podpis będzie zawierać nazwy zależne który najwyraźniej sprawia typu szablon odliczenia niemożliwe (nie mogłem znaleźć sposób na przeciążenie GetSecond dla iterator/const_iterator nawet z odroczonym zamian typ C++ 0x).

0

Mam złe rozwiązanie!

#define vertex first 
#define edge second 

Chociaż jako zło rozwiązanie to będzie zapewne powodować wielkie traumy i trudne do zdiagnozowania problemów kompilacji, gdy przypadkowo używać tych słów w innym miejscu.

Dodane do kompletności.

Nie mogę uwierzyć, że nikt inny tego nie zasugerował.

+0

Nikt inny tego nie sugerował, ponieważ jest to okropny pomysł. – user1353535