W szczególnym przypadku mapy starymi opcjami były tylko dwa: operator[]
i insert
(różne smaki insert
). Więc zacznę je wyjaśniać.
operator[]
To find-or-add operator. Spróbuje znaleźć element z danym kluczem na mapie, a jeśli istnieje, zwróci odwołanie do zapisanej wartości. Jeśli nie, utworzy nowy element wstawiony na miejscu z domyślną inicjalizacją i zwróci do niego odwołanie.
insert
Funkcja (w pojedynczy element smaku) trwa value_type
( std::pair<const Key,Value>
), to używa klucza ( first
członek) i stara się go wstawić. Ponieważ std::map
nie zezwala na duplikaty, jeśli istnieje element, nic nie wstawi.
Pierwszą różnicą między nimi jest to, że operator[]
musi być w stanie skonstruować domyślną wartość inicjowaną , a zatem nie można jej użyć w przypadku typów wartości, których nie można zainicjować domyślnie. Druga różnica między nimi polega na tym, że istnieje już element z danym kluczem. insert
Funkcja nie zmieni stan na mapie, lecz powrót iterację na elemencie (i false
wskazuje, że nie dodaje się).
// assume m is std::map<int,int> already has an element with key 5 and value 0
m[5] = 10; // postcondition: m[5] == 10
m.insert(std::make_pair(5,15)); // m[5] is still 10
W przypadku insert
argumentu jest przedmiotem value_type
, który można utworzyć na różne sposoby. Możesz bezpośrednio skonstruować go odpowiednim typem lub przekazać dowolny obiekt, z którego value_type
można go skonstruować, czyli tam, gdzie std::make_pair
wchodzi w grę, ponieważ pozwala na proste tworzenie std::pair
obiektów, chociaż prawdopodobnie nie jest to, czego chcesz ...
Efekt netto następujących połączeń jest podobny :
K t; V u;
std::map<K,V> m; // std::map<K,V>::value_type is std::pair<const K,V>
m.insert( std::pair<const K,V>(t,u) ); // 1
m.insert( std::map<K,V>::value_type(t,u) ); // 2
m.insert( std::make_pair(t,u) ); // 3
Ale tak naprawdę nie są takie same ... [1] i [2] są w rzeczywistości równoważne. W obu przypadkach kod tworzy tymczasowy obiekt tego samego typu ( std::pair<const K,V>
) i przekazuje go do insert
funkcji. insert
Funkcja stworzy odpowiedni węzeł w wyszukiwaniu binarnym drzewie i skopiuj value_type
część z argumentu do węzła. Zaletą używania value_type
jest to, że, no cóż, value_type
zawsze pasuje value_type
, nie można pomylić typu std::pair
argumentów!
Różnica polega na [3]. Ta funkcja std::make_pair
jest funkcją szablonu, która utworzy std::pair
. Podpis to:
template <typename T, typename U>
std::pair<T,U> make_pair(T const & t, U const & u );
Celowo nie podałem argumentów szablonu std::make_pair
, ponieważ jest to powszechne użycie. I implikacja jest taka, że argumenty szablonu są wywnioskowane z wywołania, w tym przypadku T==K,U==V
, więc wywołanie do std::make_pair
zwróci a std::pair<K,V>
(zwróć uwagę na brak const
). Podpis wymaga, value_type
aby był bliski, ale nie taki sam, jak wartość zwrócona z wywołania do std::make_pair
. Ponieważ jest wystarczająco blisko, utworzy tymczasową poprawnego typu i zainicjuje kopię. To z kolei zostanie skopiowane do węzła, tworząc w sumie dwie kopie.
Można to naprawić, podając argumenty szablonu:
m.insert( std::make_pair<const K,V>(t,u) ); // 4
Ale nadal jest to podatne na błędy w taki sam sposób, jak jawne wpisywanie typu w przypadku [1].
Do tego momentu istnieją różne sposoby wywoływania, insert
które wymagają utworzenia value_type
zewnętrznego i kopii tego obiektu do kontenera. Alternatywnie możesz użyć, operator[]
jeśli typ jest domyślnie możliwy do skonstruowania i przypisania (celowe ustawianie ostrości tylko w m[k]=v
) i wymaga domyślnej inicjalizacji jednego obiektu oraz skopiowania wartości do tego obiektu.
W C ++ 11, z różnymi szablonami i doskonałym przekazywaniem, istnieje nowy sposób dodawania elementów do kontenera poprzez umieszczanie (tworzenie na miejscu). Te emplace
funkcje w różnych pojemnikach zrobić w zasadzie to samo: zamiast się źródło , z którego można skopiować do pojemnika, funkcja przyjmuje parametry, które zostaną przekazane do konstruktora obiektu przechowywanego w pojemniku.
m.emplace(t,u); // 5
W [5] obiekt std::pair<const K, V>
nie jest tworzony i przekazywany emplace
, ale przekazywane są do niego odniesienia t
i u
obiekt, emplace
które przekazują je konstruktorowi value_type
podobiektu wewnątrz struktury danych. W tym przypadku niestd::pair<const K,V>
są wykonywane żadne kopie , co jest zaletą w emplace
stosunku do alternatyw C ++ 03. Tak jak w przypadku insert
tego, nie zastąpi wartości na mapie.
Interesującym pytaniem, o którym nie myślałem, jest to, jak emplace
można faktycznie zaimplementować mapę, i nie jest to prosty problem w ogólnym przypadku.