Czy w Mapach STL lepiej używać map:: insert niż []?

Jakiś czas temu miałem dyskusję z kolegą o tym, jak wstawiać wartości w STL mapy . 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 wstawka jest lepsza, ale jestem pewien, że nie chodziło tylko o preferencje stylu, a o przyczynę techniczną, taką jak efektywność. SGI stl reference po prostu mówi: "ściśle mówiąc, Ta funkcja członkowska jest zbędne: istnieje tylko dla wygody."

Czy ktoś może mi podać ten powód, czy tylko śnię, że taki istnieje?
Author: honk, 2008-11-28

13 answers

Kiedy piszesz

map[key] = value;

Nie da się powiedzieć, czyzastąpiłeś value na key, czy jeśliutworzyłeś Nowy key na value.

map::insert() utworzy tylko:

using std::cout; using std::endl;
typedef std::map<int, std::string> MyMap;
MyMap map;
// ...
std::pair<MyMap::iterator, bool> res = map.insert(MyMap::value_type(key,value));
if ( ! res.second ) {
    cout << "key " <<  key << " already exists "
         << " with value " << (res.first)->second << endl;
} else {
    cout << "created key " << key << " with value " << value << endl;
}

Dla większości moich aplikacji zazwyczaj nie obchodzi mnie, czy tworzę lub zastępuję, więc używam łatwiejszego do odczytania map[key] = value.

 241
Author: netjeff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-13 05:13:07

Mają one różną semantykę, jeśli chodzi o klucz już istniejący na mapie. Nie są więc 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, to kopiowanie konstrukcji, to będzie droższe. Czasami domyślna konstrukcja nie ma sensu i wtedy niemożliwe byłoby użycie wersji operatora [].

 53
Author: Greg Rogers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-28 16:01:34

Kolejna rzecz do zapamiętania z std::map:

myMap[nonExistingKey]; utworzy nowy wpis na mapie, z kluczem nonExistingKey initialized to a default value.

To przerażało mnie, gdy pierwszy raz to zobaczyłem(waląc głową o paskudnego pluskwę). Nie spodziewałbym się tego. Dla mnie to wygląda na operację get i nie spodziewałem się " efektu ubocznego."Preferuj map.find() Kiedy korzystasz z mapy.

 35
Author: Hawkeye Parker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-12 17:28:37

Jeśli hit wydajności domyślnego konstruktora nie jest problemem, proszę, na miłość boską, przejść z bardziej czytelną wersją.

:)

 19
Author: Torlack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-28 16:11:24

insert jest lepszy z punktu bezpieczeństwa WYJĄTKÓW.

Wyrażenie map[key] = value jest w rzeczywistości dwiema operacjami:

  1. map[key] - tworzenie elementu mapy z wartością domyślną.
  2. = value - kopiowanie wartości do tego elementu.

Wyjątek może się zdarzyć w drugim kroku. W rezultacie operacja zostanie wykonana tylko częściowo(nowy element został dodany do map, ale element ten nie został zainicjowany przez value). Sytuacji, gdy operacja nie jest kompletny, ale stan systemu jest modyfikowany, nazywa się operacja z "efektem ubocznym".

insert operacja daje silną gwarancję, czyli nie wywołuje skutków ubocznych (https://en.wikipedia.org/wiki/Exception_safety). insert jest albo całkowicie zrobione, albo pozostawia mapę w niezmodyfikowanym stanie.

Http://www.cplusplus.com/reference/map/map/insert/:

Jeśli ma być wstawiony pojedynczy element, nie ma zmian w pojemniku w przypadku wyjątek (silna gwarancja).

 14
Author: anton_rh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-21 09:34:36

Jeśli Twoja aplikacja jest krytyczna dla szybkości, radzę użyć operatora [], ponieważ tworzy łącznie 3 kopie oryginalnego obiektu, z czego 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. Alokacja pamięci jednego obiektu 2. Jedno dodatkowe wywołanie konstruktora 3. Jedno dodatkowe wywołanie destruktora 4. Jeden obiekt dealokacja pamięci

Jeśli Twoje obiekty są duże, konstruktory są typowe, destruktory uwalniają dużo zasobów, powyższe punkty liczą się jeszcze bardziej. Jeśli chodzi o czytelność, myślę, że oba są wystarczająco sprawiedliwe.

To samo pytanie przyszło mi do głowy, ale nie za czytelność, ale za szybkość. Oto przykładowy kod, dzięki któremu dowiedziałem się o punkcie, o którym wspomniałem.
class Sample
{
    static int _noOfObjects;

    int _objectNo;
public:
    Sample() :
        _objectNo( _noOfObjects++ )
    {
        std::cout<<"Inside default constructor of object "<<_objectNo<<std::endl;
    }

    Sample( const Sample& sample) :
    _objectNo( _noOfObjects++ )
    {
        std::cout<<"Inside copy constructor of object "<<_objectNo<<std::endl;
    }

    ~Sample()
    {
        std::cout<<"Destroying object "<<_objectNo<<std::endl;
    }
};
int Sample::_noOfObjects = 0;


int main(int argc, char* argv[])
{
    Sample sample;
    std::map<int,Sample> map;

    map.insert( std::make_pair<int,Sample>( 1, sample) );
    //map[1] = sample;
    return 0;
}

Wyjście po użyciu insert()Wyjście, gdy używany jest operator []

 13
Author: Rampal Chaudhary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-02 13:59:58

Teraz w c++11 myślę, że najlepszym sposobem na wstawienie pary w mapie STL jest:

typedef std::map<int, std::string> MyMap;
MyMap map;

auto& result = map.emplace(3,"Hello");

Wynik będzie parą z:

  • Pierwszy element (wym.pierwszy), wskazuje na wstawioną parę lub wskazuje na para z tym kluczem, jeśli klucz już istnieje.

  • Drugi element (wym.drugi), true jeśli wstawianie było poprawne lub coś poszło nie tak.

PS: Jeśli nie masz sprawy co do kolejności, możesz użyć STD:: unordered_map ;)

Dzięki!
 10
Author: GutiMac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-19 20:49:15

Funkcja 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 Javy, gdzie oczekiwali, że insert() będzie zachowywał się tak samo jak Map.put () w Javie, gdzie wartości są zastępowane.

 9
Author: Anthony Cramp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-29 08:31:46

Jedna uwaga jest taka, że możesz również użyć Boost.Przypisanie :

using namespace std;
using namespace boost::assign; // bring 'map_list_of()' into scope

void something()
{
    map<int,int> my_map = map_list_of(1,2)(2,3)(3,4)(4,5)(5,6);
}
 2
Author: rlbond,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-25 15:02:13

Oto kolejny przykład, pokazujący, że operator[] nadpisuje wartość klucza, jeśli istnieje, ale .insert nie nadpisuje wartości, jeśli istnieje.

void mapTest()
{
  map<int,float> m;


  for( int i = 0 ; i  <=  2 ; i++ )
  {
    pair<map<int,float>::iterator,bool> result = m.insert( make_pair( 5, (float)i ) ) ;

    if( result.second )
      printf( "%d=>value %f successfully inserted as brand new value\n", result.first->first, result.first->second ) ;
    else
      printf( "! The map already contained %d=>value %f, nothing changed\n", result.first->first, result.first->second ) ;
  }

  puts( "All map values:" ) ;
  for( map<int,float>::iterator iter = m.begin() ; iter !=m.end() ; ++iter )
    printf( "%d=>%f\n", iter->first, iter->second ) ;

  /// now watch this.. 
  m[5]=900.f ; //using operator[] OVERWRITES map values
  puts( "All map values:" ) ;
  for( map<int,float>::iterator iter = m.begin() ; iter !=m.end() ; ++iter )
    printf( "%d=>%f\n", iter->first, iter->second ) ;

}
 1
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-18 19:24:59

Jest to dość ograniczony przypadek, ale sądząc po komentarzach, które otrzymałem, myślę, że warto zwrócić uwagę.

Widziałem, jak ludzie w przeszłości używali map w formie

map< const key, const val> Map;

Aby uniknąć przypadkowego nadpisania wartości, ale potem śmiało pisać w innych bitach kodu:

const_cast< T >Map[]=val;

Ich powodem do zrobienia tego, o ile pamiętam, było to, że byli pewni, że w tych pewnych bitach kodu nie będą nadpisywać wartości map; stąd, idąc do przodu z bardziej "czytelna" Metoda [].

W rzeczywistości nigdy nie miałem żadnych bezpośrednich problemów z kodem, który został napisany przez tych ludzi, ale do dziś mocno czuję, że ryzyka-jakkolwiek małego - nie należy podejmować, kiedy można go łatwo uniknąć.

W przypadku, gdy masz do czynienia z wartościami map, które absolutnie nie mogą być nadpisane , użyj insert. Nie rób WYJĄTKÓW tylko dla czytelności.

 1
Author: dk123,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-27 04:24:43

Fakt, że funkcja std:: map insert() nie nadpisuje wartości skojarzonej z kluczem pozwala nam napisać kod wyliczenia obiektu w następujący sposób:

string word;
map<string, size_t> dict;
while(getline(cin, word)) {
    dict.insert(make_pair(word, dict.size()));
}

Jest to dość powszechny problem, gdy musimy mapować różne Nie unikalne obiekty do niektórych identyfikatorów w zakresie 0..N. identyfikatory te mogą być później wykorzystywane np. w algorytmach grafowych. Alternatywa z operator[] wyglądałaby moim zdaniem mniej czytelnie:

string word;
map<string, size_t> dict;
while(getline(cin, word)) {
    size_t sz = dict.size();
    if (!dict.count(word))
        dict[word] = sz; 
} 
 1
Author: mechatroner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-01 22:25:04

Różnica między insert() oraz operator[] zostało już dobrze wyjaśnione w innych odpowiedziach. Jednak nowe metody wstawiania dla std::map zostały wprowadzone odpowiednio z C++11 i C++17:

Pozwól, że przedstawię krótkie podsumowanie "nowe" metody wstawiania:

  • emplace(): poprawnie zastosowana metoda pozwala uniknąć niepotrzebnych operacji kopiowania lub przenoszenia, konstruując element, który ma być wstawiony na miejsce. Podobnie jak insert(), element jest wstawiany tylko wtedy, gdy w kontenerze nie ma elementu z tym samym kluczem.
  • insert_or_assign(): ta metoda jest "ulepszoną" wersją operator[]. W przeciwieństwie do operator[], insert_or_assign() nie wymaga, aby Typ wartości mapy był domyślnie konstruowalny. To pokonuje wadę wspomniany np. w odpowiedzi Grega Rogersa.
  • try_emplace(): ta metoda jest "ulepszoną" wersją emplace(). W przeciwieństwie do emplace(), try_emplace() nie modyfikuje swoich argumentów (z powodu operacji przenoszenia), jeśli wstawianie nie powiedzie się z powodu klucza już istniejącego na mapie.

Po Więcej szczegółów na temat insert_or_assign() i try_emplace() Proszę zobaczyć moją odpowiedź tutaj .

Prosty przykładowy kod na Coliru

 0
Author: honk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-10 12:29:23