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.
insertFunkcja (w pojedynczy element smaku) trwa value_type( std::pair<const Key,Value>), to używa klucza ( firstczłonek) i stara się go wstawić. Ponieważ std::mapnie 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. insertFunkcja nie zmieni stan na mapie, lecz powrót iterację na elemencie (i falsewskazuje, ż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 insertargumentu 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_typemożna go skonstruować, czyli tam, gdzie std::make_pairwchodzi w grę, ponieważ pozwala na proste tworzenie std::pairobiektó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 insertfunkcji. insertFunkcja stworzy odpowiedni węzeł w wyszukiwaniu binarnym drzewie i skopiuj value_typeczęść z argumentu do węzła. Zaletą używania value_typejest to, że, no cóż, value_typezawsze pasuje value_type , nie można pomylić typu std::pairargumentów!
Różnica polega na [3]. Ta funkcja std::make_pairjest 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_pairzwróci a std::pair<K,V>(zwróć uwagę na brak const). Podpis wymaga, value_typeaby 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, insertktóre wymagają utworzenia value_typezewnę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 emplacefunkcje 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 ti uobiekt, emplacektóre przekazują je konstruktorowi value_typepodobiektu wewnątrz struktury danych. W tym przypadku niestd::pair<const K,V> są wykonywane żadne kopie , co jest zaletą w emplacestosunku do alternatyw C ++ 03. Tak jak w przypadku inserttego, nie zastąpi wartości na mapie.
Interesującym pytaniem, o którym nie myślałem, jest to, jak emplacemożna faktycznie zaimplementować mapę, i nie jest to prosty problem w ogólnym przypadku.