Ideą uzyskania iteratora do wartości jest użycie go w algorytmach STL, na przykład przecięcie kluczy dwóch map. Rozwiązanie wykorzystujące Boost nie pozwala na to, ponieważ utworzy iterator Boost. Najgorsza odpowiedź otrzymuje najwięcej głosów!
Odpowiedzi:
70
Jeśli naprawdę potrzebujesz ukryć wartość zwracaną przez „prawdziwy” iterator (na przykład dlatego, że chcesz używać swojego iteratora klucza ze standardowymi algorytmami, aby działały na klawiszach zamiast na parach), spójrz na funkcję Boost transform_iterator .
[Wskazówka: patrząc na dokumentację Boost dla nowej klasy, najpierw przeczytaj "przykłady" na końcu. Masz wtedy sportową szansę dowiedzieć się, o czym u licha mówi reszta :-)]
Ale wtedy wystawienie iteratora wektora na zewnątrz będzie naprawdę złym pomysłem.
Naveen
Nie ujawniaj iteratora. Wystarczy podać klucze w wektorze
aJ.
5
Możesz to zrobić zamiast tego: const Key& k(iter->first);
strickli
17
Dwie rzeczy, to odpowiedź na pytanie OP z dokładnie taką odpowiedź już wiedział i nie szukałem, po drugie ta metoda nie pomoże, jeśli chcesz zrobić coś takiego: std::vector<Key> v(myMap.begin(), myMap.end()).
Andreas Magnusson
Nie konwertuj kluczy na wektor. Stworzenie nowego wektora jest sprzeczne z celem iteracji, który ma być szybki i niczego nie alokować. Ponadto będzie to powolne w przypadku dużych zestawów.
Kevin Chen
85
W C ++ 11 składnia iteracji jest prosta. Nadal iterujesz po parach, ale dostęp tylko do klucza jest łatwy.
Niestety standard C ++ 17 wymaga zadeklarowania valuezmiennej, nawet jeśli jej nie używasz ( std::ignoreponieważ std::tie(..)nie działa, zobacz tę dyskusję ).
Dlatego niektóre kompilatory mogą ostrzec Cię o nieużywanej valuezmiennej! Ostrzeżenia w czasie kompilacji dotyczące nieużywanych zmiennych są nie do pomyślenia w przypadku żadnego kodu produkcyjnego. Dlatego może to nie mieć zastosowania w przypadku niektórych wersji kompilatora.
czy nie mógłbyś przypisać go do std :: ignore w zasadzie? Czy to faktycznie zaszkodzi wydajności w skompilowanym kodzie, czy też w rzeczywistości dałoby to nic? (Nie mam na myśli wiązania, ale raczej jako działanie w pętli)
KotoroShinoto
Od C ++ 17 możesz także używać [[może_unused]]. To pomija ostrzeżenie. W ten sposób:for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
arhuaco
15
Poniżej bardziej ogólne rozwiązanie oparte na szablonach, do którego odniósł się Ian ...
Gdy nie jest to jawne begini nie endjest potrzebne, np. Do zapętlania zakresu, pętlę po kluczach (pierwszy przykład) lub wartości (drugi przykład) można uzyskać za pomocą
#include<boost/range/adaptors.hpp>
map<Key,Value> m;for(auto k : boost::adaptors::keys(m))
cout << k << endl;for(auto v : boost::adaptors::values(m))
cout << v << endl;
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();for(; iter != endIter;++iter){
type key = iter->first;.....}
Tak, wiem, problem polega na tym, że mam klasę A {public: // chciałbym ujawnić iterator nad kluczami mapy prywatnej tutaj private: map <>};
Bogdan Balan
W takim przypadku myślę, że możesz stworzyć std :: list używając std :: trasnform i pobierając tylko klucze z mapy. Następnie możesz ujawnić iterator listy, ponieważ wstawienie większej liczby elementów do listy nie unieważni istniejących iteratorów.
Naveen
3
Jeśli potrzebujesz iteratora, który po prostu zwraca klucze, musisz zawinąć iterator mapy we własnej klasie, która zapewnia żądany interfejs. Możesz zadeklarować nową klasę iteratora od podstaw, tak jak tutaj , używając istniejących konstrukcji pomocniczych. Ta odpowiedź pokazuje, jak używać funkcji Boost, transform_iteratoraby zawinąć iterator w taki, który zwraca tylko wartości / klucze.
Bez Boost można to zrobić w ten sposób. Byłoby miło, gdybyś mógł napisać operator rzutowania zamiast metody getKeyIterator (), ale nie mogę go skompilować.
Wiem, że to nie odpowiada na twoje pytanie, ale jedną z opcji, na którą możesz chcieć spojrzeć, jest po prostu posiadanie dwóch wektorów z tym samym indeksem będącym informacją „połączoną”.
jeśli chcesz liczyć nazwy według nazwy, po prostu wykonaj szybką pętlę for przez vName.size (), a kiedy ją znajdziesz, jest to indeks dla vNameCount, którego szukasz.
Jasne, może to nie zapewniać całej funkcjonalności mapy, a zależność może być lepsza lub nie, ale może być łatwiej, jeśli nie znasz kluczy i nie powinieneś dodawać zbyt dużego przetwarzania.
Pamiętaj tylko, że kiedy dodajesz / usuwasz z jednego, musisz to zrobić z drugiego, inaczej sprawy zwariują heh: P
Odpowiedzi:
Jeśli naprawdę potrzebujesz ukryć wartość zwracaną przez „prawdziwy” iterator (na przykład dlatego, że chcesz używać swojego iteratora klucza ze standardowymi algorytmami, aby działały na klawiszach zamiast na parach), spójrz na funkcję Boost transform_iterator .
[Wskazówka: patrząc na dokumentację Boost dla nowej klasy, najpierw przeczytaj "przykłady" na końcu. Masz wtedy sportową szansę dowiedzieć się, o czym u licha mówi reszta :-)]
źródło
mapa to kontener asocjacyjny. Stąd iterator jest parą kluczy, val. JEŚLI potrzebujesz tylko kluczy, możesz zignorować część wartości z pary.
EDYCJA:: W przypadku, gdy chcesz wystawić tylko klucze na zewnątrz, możesz przekonwertować mapę na wektor lub klucze i wystawić.
źródło
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.W C ++ 11 składnia iteracji jest prosta. Nadal iterujesz po parach, ale dostęp tylko do klucza jest łatwy.
źródło
Bez wzmocnienia
Możesz to zrobić, po prostu rozszerzając iterator STL dla tej mapy. Na przykład mapowanie ciągów znaków na wartości int:
Możesz również wykonać to rozszerzenie w szablonie , aby uzyskać bardziej ogólne rozwiązanie.
Używasz iteratora dokładnie tak, jak używasz iteratora listy, z wyjątkiem tego, że iterujesz po mapach
begin()
iend()
.źródło
template<typename C> class key_iterator : public C::iterator
itd.W C ++ 17 możesz użyć strukturalnego powiązania wewnątrz pętli for opartej na zakresie (dostosowując odpowiednio odpowiedź Johna H. ):
Niestety standard C ++ 17 wymaga zadeklarowania
value
zmiennej, nawet jeśli jej nie używasz (std::ignore
ponieważstd::tie(..)
nie działa, zobacz tę dyskusję ).Dlatego niektóre kompilatory mogą ostrzec Cię o nieużywanej
value
zmiennej! Ostrzeżenia w czasie kompilacji dotyczące nieużywanych zmiennych są nie do pomyślenia w przypadku żadnego kodu produkcyjnego. Dlatego może to nie mieć zastosowania w przypadku niektórych wersji kompilatora.źródło
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Poniżej bardziej ogólne rozwiązanie oparte na szablonach, do którego odniósł się Ian ...
Wszystkie kredyty należą się Ianowi ... Dzięki, Ian.
źródło
Szukasz map_keys , dzięki czemu możesz pisać takie rzeczy jak
źródło
BOOST_FOREACH(const key_t& key, ...
Oto przykład, jak to zrobić za pomocą transform_iterator Boost
źródło
Gdy nie jest to jawne
begin
i nieend
jest potrzebne, np. Do zapętlania zakresu, pętlę po kluczach (pierwszy przykład) lub wartości (drugi przykład) można uzyskać za pomocąźródło
Chcesz to zrobić?
źródło
Jeśli potrzebujesz iteratora, który po prostu zwraca klucze, musisz zawinąć iterator mapy we własnej klasie, która zapewnia żądany interfejs. Możesz zadeklarować nową klasę iteratora od podstaw, tak jak tutaj , używając istniejących konstrukcji pomocniczych. Ta odpowiedź pokazuje, jak używać funkcji Boost,
transform_iterator
aby zawinąć iterator w taki, który zwraca tylko wartości / klucze.źródło
Mógłbyś
std::map<K,V>::iterator
std::transform
swojegomap.begin()
Domap.end()
zeboost::bind( &pair::second, _1 )
funktora->second
członka podczas iteracji zfor
pętlą.źródło
Ta odpowiedź jest jak rodrigob, z wyjątkiem braku
BOOST_FOREACH
. Zamiast tego możesz użyć zakresu c ++ opartego na.źródło
Bez Boost można to zrobić w ten sposób. Byłoby miło, gdybyś mógł napisać operator rzutowania zamiast metody getKeyIterator (), ale nie mogę go skompilować.
źródło
Dla potomnych, a ponieważ próbowałem znaleźć sposób na stworzenie zakresu, alternatywą jest użycie boost :: adapters :: transform
Oto mały przykład:
Jeśli chcesz iterować po wartościach, użyj
t.second
w lambdzie.źródło
Wiele dobrych odpowiedzi tutaj, poniżej jest podejście wykorzystujące kilka z nich, które pozwalają napisać to:
Jeśli zawsze tego chciałeś, oto kod dla MapKeys ():
źródło
Zaadoptowałem odpowiedź Iana do pracy ze wszystkimi typami map i naprawiłem zwracanie odniesienia dla
operator*
źródło
Wiem, że to nie odpowiada na twoje pytanie, ale jedną z opcji, na którą możesz chcieć spojrzeć, jest po prostu posiadanie dwóch wektorów z tym samym indeksem będącym informacją „połączoną”.
Więc w ...
jeśli chcesz liczyć nazwy według nazwy, po prostu wykonaj szybką pętlę for przez vName.size (), a kiedy ją znajdziesz, jest to indeks dla vNameCount, którego szukasz.
Jasne, może to nie zapewniać całej funkcjonalności mapy, a zależność może być lepsza lub nie, ale może być łatwiej, jeśli nie znasz kluczy i nie powinieneś dodawać zbyt dużego przetwarzania.
Pamiętaj tylko, że kiedy dodajesz / usuwasz z jednego, musisz to zrobić z drugiego, inaczej sprawy zwariują heh: P
źródło