2009-09-30 8 views
16

Esiste un modo per rinominare la prima e la seconda funzione di accesso di un iteratore di mappe. Capisco che abbiano questi nomi a causa della coppia sottostante che rappresenta la chiave e il valore, ma mi piacerebbe che gli iteratori fossero un po 'più leggibili. Penso che questo potrebbe essere possibile usando un adattatore iteratore, ma non sono sicuro di come implementarlo.Rinomina primo e secondo di un iteratore di mappe

Si prega di notare che non posso usare boost.

Esempio di ciò che intendo:

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

Divertente: al momento ci sono molte domande relative al grafico: http://stackoverflow.com/questions/1499878/use-a-graph-library-node-network-library-or-roll-my- proprio, http://stackoverflow.com/questions/1499217/boost-graph-as-basis-for-a-simple-dag-graph –

+0

Perché non puoi usare boost? –

risposta

15

Se siete solo preoccupati per la leggibilità si potrebbe fare qualcosa di simile:

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

E poi:

Vertex v = adjacency(it).vertex; 
5

Certo, reimplementare o avvolgere iteratore, ma è valsa la pena?

Vertex& v = it->first; 

essere più facile?

+3

Ricorda inoltre che l'uso del primo e del secondo std :: map iterator è un tale idioma che confonderai chiunque leggendo il tuo codice - un po '. – phoku

+3

Questo è vero, ma dal punto di vista mentale, 'prima' e' seconda' sono abbastanza confusi ;-) –

7

Sfortunatamente, no. Quello che faccio di solito è questo:

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

Per la leggibilità. All'interno del vostro ciclo si può anche dire

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

Non è possibile rinomina i membri, ma puoi avere alcune funzioni per aiutarti.

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

Poi, invece di it->vertex come si vuole, si può fare vertex(it)

+1

+1 Aggiunge anche sovraccarichi per const_iterators. – UncleBens

1

Io non ve lo consiglio di utilizzare questo, ma sembra funzionare, almeno nella misura minima del programma di test facendo quello che volevo/mi aspettavo:

#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

Mi piaceva la soluzione di KeithB con funzioni gratuite. Tuttavia, una soluzione più riutilizzabile potrebbe essere piacevole.

Che dire di oggetti funzione che l'accesso prima o la seconda, come si può assegnare un nome al nulla le istanze che ti piace:

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

questo è il meglio che potessi fare. Ho anche provato a far sì che quei funtori accettino direttamente l'iteratore, ma in un modo o nell'altro ciò significa che la firma conterrà nomi dipendenti che apparentemente rendono impossibile la deduzione del tipo di modello (non ho trovato il modo di sovraccaricare GetSecond per iterator/const_iterator anche con il ritorno differito tipo di C++ 0x).

0

Ho una soluzione malvagia!

#define vertex first 
#define edge second 

Anche se come una soluzione male che senza dubbio causare grande trauma e difficile da diagnosticare problemi di compilazione quando si utilizza accidentalmente quelle parole altrove.

Aggiunto per completezza.

Non riesco a credere che nessun altro abbia suggerito questo.

+0

Nessun altro ha suggerito questo perché è una pessima idea. – user1353535

Problemi correlati