Jakiś czas temu rozmawiałem z kolegą o tym, jak wstawiać wartości do map STL . Wolałem,
map[key] = value;
ponieważ wydaje się to naturalne i czytelne, podczas gdy on wolał
map.insert(std::make_pair(key, value))
Właśnie go zapytałem i żadne z nas nie pamięta, dlaczego wkładka jest lepsza, ale jestem pewien, że nie była to tylko preferencja stylowa, a raczej powód techniczny, taki jak wydajność. Odniesienia SGI STL po prostu mówi „Ściśle mówiąc, ta funkcja jest niepotrzebna członkiem. Istnieje tylko dla wygody”
Czy ktoś może mi powiedzieć ten powód, czy tylko marzę, że jest taki powód?
Odpowiedzi:
Kiedy piszesz
nie ma sposobu, aby stwierdzić, czy otrzymuje się
value
zakey
lub jeśli utworzony nowykey
zvalue
.map::insert()
utworzy tylko:W przypadku większości moich aplikacji zwykle nie dbam o to, czy tworzę, czy wymieniam, więc używam łatwiejszego do odczytania
map[key] = value
.źródło
(res.first)->second
zamiastvalue
w drugim przypadku.else
ponieważ myślę, że używanievalue
jest bardziej przejrzyste niż iterator. Tylko jeśli typ wartości miałby nietypowy egzemplarz ctor lub op ==, byłby inny, a ten typ spowodowałby inne problemy z użyciem kontenerów STL, takich jak map.map.insert(std::make_pair(key,value))
powinno byćmap.insert(MyMap::value_type(key,value))
. Typ zwracany zmake_pair
nie jest zgodny z typem pobranym,insert
a obecne rozwiązanie wymaga konwersjioperator[]
, po prostu porównaj rozmiar przed i po. Imho może wywoływaćmap::operator[]
tylko domyślne typy konstrukcyjne, jest o wiele ważniejsze.Obie mają różną semantykę, jeśli chodzi o klucz już istniejący na mapie. Więc nie są tak naprawdę bezpośrednio porównywalne.
Ale wersja operatora [] wymaga domyślnego skonstruowania wartości, a następnie przypisania, więc jeśli jest to droższe niż konstrukcja kopiowania, to będzie droższe. Czasami domyślna konstrukcja nie ma sensu, a wtedy korzystanie z wersji operator [] byłoby niemożliwe.
źródło
Kolejna rzecz, na którą warto zwrócić uwagę
std::map
:myMap[nonExistingKey];
utworzy nowy wpis na mapie, kluczowany dononExistingKey
zainicjowania na wartość domyślną.To mnie przeraziło, gdy zobaczyłem to po raz pierwszy (uderzając głową o paskudnego robaka). Nie spodziewałbym się tego. Dla mnie wygląda to na operację get i nie spodziewałem się „efektu ubocznego”. Wolę,
map.find()
gdy wychodzisz z mapy.źródło
Jeśli hit wydajności domyślnego konstruktora nie stanowi problemu, proszę, na miłość boską, przejdź do bardziej czytelnej wersji.
:)
źródło
insert
jest lepszy z punktu widzenia bezpieczeństwa wyjątkowego.Wyrażenie
map[key] = value
to tak naprawdę dwie operacje:map[key]
- tworzenie elementu mapy z wartością domyślną.= value
- kopiowanie wartości do tego elementu.Wyjątek może wystąpić na drugim etapie. W rezultacie operacja zostanie wykonana tylko częściowo (nowy element został dodany do mapy, ale tego elementu nie zainicjowano
value
). Sytuacja, w której operacja nie jest zakończona, ale stan systemu jest zmodyfikowany, nazywana jest operacją z „efektem ubocznym”.insert
operacja daje silną gwarancję, oznacza, że nie ma skutków ubocznych ( https://en.wikipedia.org/wiki/Exception_safety ).insert
jest całkowicie gotowy lub pozostawia mapę w niezmodyfikowanym stanie.http://www.cplusplus.com/reference/map/map/insert/ :
źródło
Jeśli twoja aplikacja jest krytyczna pod względem szybkości, doradzę za pomocą operatora [], ponieważ tworzy ona w sumie 3 kopie oryginalnego obiektu, z których 2 są obiektami tymczasowymi i prędzej czy później zniszczonymi jako.
Ale w insert () tworzone są 4 kopie oryginalnego obiektu, z czego 3 są obiektami tymczasowymi (niekoniecznie „tymczasowymi”) i są niszczone.
Co oznacza dodatkowy czas na: 1. Przydział pamięci jednego obiektu 2. Jedno dodatkowe wywołanie konstruktora 3. Jedno dodatkowe wywołanie destruktora 4. Deallokacja pamięci jednego obiektu
Jeśli twoje obiekty są duże, konstruktory są typowe, destruktory uwalniają wiele zasobów, powyżej punktów liczy się jeszcze więcej. Jeśli chodzi o czytelność, uważam, że oba są wystarczająco uczciwe.
To samo pytanie przyszło mi do głowy, ale nie z powodu czytelności, ale szybkości. Oto przykładowy kod, dzięki któremu dowiedziałem się o punkcie, o którym wspomniałem.
źródło
insert
musi przeprowadzić to samo wyszukiwanie, więc nie ma w tym różnicy[]
(ponieważ klucze map są unikalne).Teraz w c ++ 11 Myślę, że najlepszym sposobem wstawienia pary do mapy STL jest:
Wynik będzie para z:
Pierwszy element (score.first) wskazuje na wstawioną parę lub wskaż parę za pomocą tego klucza, jeśli klucz już istnieje.
Drugi element (wynik. Drugi), prawda, jeśli wstawienie było prawidłowe lub fałszywe, coś poszło nie tak.
PS: Jeśli nie masz wątpliwości co do kolejności, możesz użyć std :: unordered_map;)
Dzięki!
źródło
Gotcha z map :: insert () polega na tym, że nie zastąpi wartości, jeśli klucz już istnieje na mapie. Widziałem kod C ++ napisany przez programistów Java, w których spodziewali się, że insert () będzie zachowywać się tak samo jak Map.put () w Javie, gdzie wartości są zamieniane.
źródło
Jedna uwaga jest taka, że możesz także użyć funkcji Boost .
źródło
Oto kolejny przykład pokazujący, że
operator[]
nadpisuje wartość klucza, jeśli istnieje, ale.insert
nie nadpisuje wartości, jeśli istnieje.źródło
Jest to raczej ograniczony przypadek, ale sądząc z otrzymanych komentarzy, myślę, że warto to zauważyć.
W przeszłości widziałem ludzi używających map w formie
aby uniknąć przypadków przypadkowego zastąpienia wartości, ale pisz dalej w kilku innych fragmentach kodu:
Powodem, dla którego to zrobiłem, jak pamiętam, było to, że byli pewni, że w tych określonych fragmentach kodu nie zamierzają nadpisywać wartości map; stąd kontynuacja bardziej „czytelnej” metody
[]
.Tak naprawdę nigdy nie miałem bezpośrednich problemów z kodem napisanym przez tych ludzi, ale do tej pory mocno odczuwam, że ryzyko - jakkolwiek małe - nie powinno być podejmowane, gdy można go łatwo uniknąć.
W przypadkach, gdy masz do czynienia z wartościami mapy, których absolutnie nie wolno nadpisywać, użyj
insert
. Nie rób wyjątków tylko dla czytelności.źródło
insert
(nieinput
), ponieważconst_cast
spowoduje to zastąpienie dowolnej poprzedniej wartości, co jest bardzo niestałe. Lub nie oznaczaj typu wartości jakoconst
. (Tego rodzaju rzeczy są zwykle ostatecznym rezultatemconst_cast
, więc prawie zawsze jest to czerwona flaga wskazująca błąd gdzie indziej.)insert
w przypadkach, w których chcesz zapobiec zastąpieniu wartości. (Właśnie zmieniłinput
sięinsert
- dzięki)const_cast<T>(map[key])
to: 1. [] jest bardziej czytelny, 2. są pewni pewnych fragmentów kodu, że nie będą nadpisywać wartości, i 3. nie chcą, aby inne fragmenty nieświadomego kodu nadpisały swoje wartości - stądconst value
.const_cast
wydaje się bardziej niż negować dodatkową „czytelność”[]
, a tego rodzaju pewność jest prawie wystarczającą podstawą do zwolnienia programisty. Trudne warunki działania rozwiązują kuloodporne konstrukcje, a nie przeczucia.Fakt, że
insert()
funkcja std :: map nie zastępuje wartości powiązanej z kluczem, pozwala nam napisać kod wyliczenia obiektu w następujący sposób:Jest to dość powszechny problem, gdy musimy mapować różne nieunikalne obiekty na niektóre identyfikatory w zakresie 0..N. Te identyfikatory można później wykorzystać na przykład w algorytmach graficznych.
operator[]
Moim zdaniem alternatywa z wyglądałaby mniej czytelnie:źródło