2009-09-10 29 views
12

Chciałbym utworzyć std::map, który zawiera std::vector z iteratorów w sobie, aby zaimplementować prostą strukturę graficzną opartą na listach przyległych.Mapa STL na siebie?

Jednak deklaracja typ ma mnie stumped: wydaje się, trzeba całą definicję typu map, aby uzyskać typ iteracyjnej wspomnianych map, tak:

map< int, Something >::iterator MyMap_it; // what should Something be? 
map< int, vector<MyMap_it> > MyMap_t; 

Czy istnieje jakiś częściowej mapy iterator typ I można uzyskać za pomocą tylko typu klucza, więc mogę zadeklarować pełną mapę?

+2

Interesting..sounds jak nieskończonej rekurencji. – Naveen

+0

Tak właśnie myślałem. – GManNickG

+1

Tylko okrągły wskaźnik ... nie ma rekursji, chyba że <> :: iterator spróbuje zrobić coś znaczącego z argumentem typu.Które byłoby całkowicie legalne, aby to zrobić, po prostu nie dzieje się w GCC + SGI STL. – Potatoswatter

Odpowiedz

14

Można użyć deklaracji forward nowego typu.

class MapItContainers; 
typedef map<int, MapItContainers>::iterator MyMap_it; 

class MapItContainers 
{ 
public: 
vector<MyMap_it> vec; 
}; 

Przy takim ustawieniu kompilator powinien pozwolić mu uciec. To nie jest takie piękne, ale szczerze mówiąc, nie sądzę, aby można było łatwo złamać samoczynne odwoływanie się.

+0

gcc 4.4.1 wygląda na to, że nie ma w tym barfa, pozwala mi nawet tworzyć wystąpienia 'map . – Omnifarious

+0

Prawdopodobnie dlatego, że iterator używa tylko wskaźników/odwołań do typu drugiego szablonu argumentu, więc wystarczy deklaracja przekazania. – Kos

5

Nie zbyt brzydki, biorąc pod uwagę ...

Działa to w GCC 4.0.1 i kompiluje grzywny w Comeau trybie ścisłym.

Definicje szablonów są analizowane i odłożone, dopóki nie zostaną utworzone. Kompilator nawet nie widzi, co to jest rec_map_iterator, dopóki nie nadszedł czas, aby go stworzyć, do czasu gdy wie, jak to zrobić;

template< class key > 
struct rec_map; 

template< class key > 
struct rec_map_iterator : rec_map<key>::iterator { 
    rec_map_iterator(typename rec_map<key>::iterator i) 
    : rec_map<key>::iterator(i) {} 
}; 

template< class key > 
struct rec_map : map< key, vector< rec_map_iterator<key> > > {}; 

Oto program testowy, którego użyłem.

#include <iostream> 
#include <map> 
#include <vector> 

using namespace std; 

template< class key > 
struct rec_map; 

template< class key > 
struct rec_map_iterator : rec_map<key>::iterator { 
    rec_map_iterator(typename rec_map<key>::iterator i) 
    : rec_map<key>::iterator(i) {} 
}; 

template< class key > 
struct rec_map : map< key, vector< rec_map_iterator<key> > > {}; 

int main(int argc, char ** argv) { 
    rec_map<int> my_map; 

    my_map[4]; 
    my_map[6].push_back(my_map.begin()); 

    cerr << my_map[6].front()->first << endl; 

    return 0; 
} 
+0

+1 za przerażenie. Nigdy nie używałbym tego w kodzie produkcyjnym, ale rozwiązanie firmy Nasmorns jest znacznie prostsze. – hirschhornsalz

+0

mine pozwala ci pisać prostszy kod oprócz jednorazowej sztuczki. Być może mniej elegancki, ale myślę, że jest bardziej w duchu C++; v). – Potatoswatter

+0

Dlaczego by nie "szablonu struct rec_map_iterator: rec_map :: iterator' wymagają' typename' przed 'rec_map :: iterator'? To samo dotyczy podstawowej listy inicjalizacji. (FWIW, como zgadza się z GCC, ale nie rozumiem, dlaczego tak by było). – sbi

2

mi się nie podoba pochodzący z pojemnika w mojej poprzedniej odpowiedzi, więc tutaj jest alternatywa:

template< class key > 
struct rec_map_gen { 
    struct i; 
    typedef map< key, vector<i> > t; 
    struct i : t::iterator { 
     i(typename t::iterator v) 
     : t::iterator(v) {} 
    }; 
}; 

Teraz trzeba użyć rec_map_gen<int>::t, rec_map_gen<int>::t::iterator, etc, ale także mieć dostęp do wszystkich std::map - konstruktorów. Szkoda, że ​​C++ nie pozwala na szablonowe szablony.

Użycie wyprowadzonego typu iteratora powinno być OK. Nadal można zainicjować odwrotny iterator z elementu tej struktury, na przykład.

2

Oprócz odpowiedzi Potatoswatter, o ile nie przeszkadza konieczności odnoszą się do całych matrycy typu map wiele razy, tylko trzeba podklasy iteracyjnej i nie wymaga żadnych wstępnych deklaracji:

template<class key> 
struct rec_map_iterator : map<key, vector<rec_map_iterator<key> > >::iterator 
{ 
    rec_map_iterator(typename map<key, vector<rec_map_iterator<key> > >::iterator i) 
     : map<key, vector<rec_map_iterator<key> > >::iterator(i) 
    {} 
}; 

Następnie korzystać z pełnej typ:

map<int, vector<rec_map_iterator<int>>> m; 

również tutaj jest aktualizacja (mój ulubiony tak daleko) dla C++ 11 deklarując rec_map jako alias, który można matrycy:

template<class key> 
struct rec_map_iterator; 

template<class key> 
using rec_map = map<key, vector<rec_map_iterator<key>>>; 

template<class key> 
struct rec_map_iterator : rec_map<key>::iterator 
{ 
    rec_map_iterator(typename rec_map<key>::iterator i) 
     : rec_map<key>::iterator(i) 
    {} 
}; 

To działa tak samo jak w wersji Potatoswatter:

rec_map<int> my_map; 
+0

Jest to podobne do mojej * drugiej * odpowiedzi :), ale faktoring C++ 11 jest ładniejszy. – Potatoswatter