Domanda Qual è la differenza tra unordered_map :: emplace e unordered_map :: insert in C ++?


Qual è la differenza tra unordered_map :: emplace e unordered_map :: insert in C ++?


17
2017-10-19 01:31


origine


risposte:


unordered_map::insert copia o sposta una coppia chiave-valore nel contenitore. È sovraccarico per accettare reference-to-const o un riferimento di rvalue:

std::pair<iterator,bool> insert(const std::pair<const Key, T>& value);

template<class P>
std::pair<iterator,bool> insert(P&& value);

unordered_map::emplace ti consente di evitare copie o mosse non necessarie costruendo l'elemento in posizione. Usa l'inoltro perfetto e un modello variadico per inoltrare argomenti al costruttore della coppia chiave-valore:

template<class... Args>
std::pair<iterator,bool> emplace(Args&&... args);

Ma c'è una grande quantità di sovrapposizione tra le due funzioni. emplace può essere utilizzato per inoltrare al costruttore copia / sposta della coppia chiave-valore che consente di utilizzarlo come insert voluto. Questo significa che l'uso di emplace non garantisce che eviterai copie o spostamenti. Anche la versione di insert che prende un riferimento-valore è in realtà un modello e accetta qualsiasi tipo P tale che la coppia chiave-valore è costruibile da P.

Scott Meyers dice: 

In linea di principio, le funzioni di emplacement dovrebbero a volte essere più efficienti   rispetto alle loro controparti di inserimento, e non dovrebbero mai essere meno   efficiente.

( Modificare: Howard Hinnant corse alcuni esperimenti quello ha mostrato a volte insert è più veloce di emplace)

Se si vuole davvero copiare / spostare nel contenitore, potrebbe essere opportuno utilizzarlo insert perché è più probabile che si verifichi un errore di compilazione se si inoltrano argomenti non corretti. Devi essere più attento a passare gli argomenti corretti alle funzioni di posizionamento.

La maggior parte delle implementazioni di unordered_map::emplace causerà l'allocazione dinamica della memoria per la nuova coppia, anche se la mappa contiene già un elemento con quella chiave e la emplace avrà esito negativo. Ciò significa che se ci sono buone possibilità che a emplace non riuscirai a ottenere prestazioni migliori usando l'inserimento per evitare inutili allocazioni dinamiche della memoria.

Piccolo esempio:

#include <unordered_map>
#include <iostream>

int main() {
  auto employee1 = std::pair<int, std::string>{1, "John Smith"};

  auto employees = std::unordered_map<int, std::string>{};

  employees.insert(employee1);  // copy insertion
  employees.insert(std::make_pair(2, "Mary Jones"));  // move insertion 
  employees.emplace(3, "James Brown");  // construct in-place

  for (const auto& employee : employees)
    std::cout << employee.first << ": " << employee.second << "\n";
}

Edit2: Su richiesta. È anche possibile usare unordered_map::emplace con una chiave o un valore che richiede più di un parametro del costruttore. Usando il std::pair  costruttore a tratti puoi comunque evitare copie o spostamenti non necessari.

#include <unordered_map>
#include <iostream>

struct Employee {
  std::string firstname;
  std::string lastname;
  Employee(const std::string& firstname, const std::string& lastname) 
  : firstname(firstname), lastname(lastname){}    
};

int main() {
  auto employees = std::unordered_map<int, Employee>{};
  auto employee1 = std::pair<int, Employee>{1, Employee{"John", "Smith"}};

  employees.insert(employee1);  // copy insertion
  employees.insert(std::make_pair(2, Employee{"Mary", "Jones"}));  // move insertion
  employees.emplace(3, Employee("Sam", "Thomas")); // emplace with pre-constructed Employee
  employees.emplace(std::piecewise_construct,
                    std::forward_as_tuple(4),
                    std::forward_as_tuple("James", "Brown"));  // construct in-place
}

28
2017-10-19 03:34