2014-04-28 10 views
12

Załóżmy, że mam std::map<int, int>, czy można to bezpiecznie wykonać?Bezpieczne w użyciu + = operator, aby utworzyć nowy wpis std :: map za pomocą []?

std::map<int, int> m_map; 
m_map[0] += 1; 

Jeśli klucz 0 nie istniała na mapie, kiedy to zrobić, jak to, co wartość, aby dodać do 1?

Mam nadzieję, że std :: map obsłuży to, wykonując = zamiast += w przypadku, gdy wartość tworzy nowy wpis na mapie. To oszczędzi mnie od konieczności wykonywania.

std::map<int, int>::iterator it = m_map.find(0); 
if(it != m_map.end()) { 
    it->second += 1; 
} 
else { 
    m_map[0] = 1; 
} 
+0

Prawdopodobny duplikat: http://stackoverflow.com/questions/4523959/stdmap-default-value-for-build-in-type – JBentley

Odpowiedz

18

Element umieszczony w mapie na invoke z operator[] powodu klucza poprzednio niezmapowanego jest wartość zainicjowany. Jeśli nie widzisz tej składni, rozważ specjalne znaczenie w poniższym fragmencie kodu.Pareny są ważne. Wprowadzają inną podróż w dół drzewa inicjacji niż domyślna inicjalizacja. Oba są ważne; oba są określone przez standard językowy.

int i = int(); 

Jak się okazuje, inicjalizacji wartość dla skalarów (w tym wskaźników) w końcu ulega zerowej inicjalizacji. Chociaż nieparzysty wygląd, wartość wcześniejszego fragmentu-inicjuje wystąpienie int, które staje się zerową inicjacją, ponieważ int jest skalarem, a następnie kopiuje je do i. (Aby być uczciwym, prawie na pewno będzie jakaś przesada, ale podstawy są tak prezentowane).

Niezależnie od tego, z powodu tej funkcji, można mieć pewność, kiedy to zrobić:

m_map[0] += 1; 

lub nawet to:

++m_map[0]; 

jeśli indeks nie został odwzorowany przed, a value- zainicjalizowany element zostanie dodany, co spowoduje zerowanie inicjalizacji dla skalarów, co oznacza, że ​​oficjalnie rozpoczniesz z zero.

Warto wspomnieć, że podobna aktywność ma miejsce dla dowolnego typu z domyślnie zadeklarowanym konstruktorem. Czy to trywialne, czy nie, dzieje się coś ciekawego.

struct S { int a; int b; }; 
std::map<int, S> mymap; 

++mymap[0].a; 

Jest członkiem a odwzorowane na 0 w naszym pojemniku niezawodnie 1 po powyższych sporządzi? Tak, to jest. Ponadto, rozważ to:

struct S { int a; std::string str; }; 
std::map<int, S> mymap; 

++mymap[0].a; 

Teraz S ma nietrywialne niejawny konstruktor (ma się, jak to musi skonstruować str). Ale czy członek a zmapowany do 0 w naszym pojemniku jest niezawodny od zera (a zatem 1 po powyższej linii)? Tak, to jest.

Jeśli ciekawi Cię różne ścieżki inicjalizacji, see this question and answer. Lub przejrzyj standard C++ 11, w szczególności: C++ 11 § 8.5 Inicjatory, (p5, p7, p10). Warto przeczytać.

2

Mapa jest leniwie zainicjować z domyślnego konstruktora (zero dla wszystkich int)

Więc 0 nie istniała na mapie, to być inicjowane zawierać wartość 0 i 1 zostaną dodane po += 1.

Możesz więc użyć górnej wersji kodu bez żadnych obaw.

Cytowanie Wpływ wywołującego map_obj [K] z cplusplus.com

Jeśli k pasuje klucz elementu w kontenerze, funkcję Zwraca odwołanie do jego odwzorowanym wartości.

Jeśli k nie odpowiada kluczowi żadnego elementu w kontenerze, funkcja wstawia nowy element z tym kluczem i zwraca odniesienie do jego zmapowanej wartości. Zauważ, że to zawsze zwiększa rozmiar pojemnika o jeden, nawet jeśli żadna zmapowana wartość nie jest przypisana do elementu (element jest konstruowany przy użyciu jego domyślnego konstruktora).

4

Tak to jest w porządku, nowe wpisy domyślnie wartość value_type() który jest 0 dla int.

Wciąż ma wartość +=, ale 0 += 1 podaje 1.

7

Gwarantuje się, że otrzymasz 1 nawet z krótszym fragmentem.

Kluczem jest to, że operator[] musi utworzyć element w mapie przed powrocie odniesienie do niego, więc do czasu masz do += element już istnieje, a jeśli miał zostać utworzony teraz, z wartość zero (ponieważ elementy mapy są inicjowane wartością).

(nawiasem mówiąc, to jest powód, dlaczego, kiedy używasz std::map z typem klasy jako wartość, to musi mieć konstruktora domyślnego, jeśli chcesz używać operator[], nawet jeśli bezpośrednio przypisać obiekt do niego)

Powiązane problemy