В чем разница между std::unordered_map::emplace
и std::unordered_map::insert
в C++?
В чем разница между unordered_map:: emplace и unordered_map:: insert в С++?
Ответ 1
unordered_map::insert
копирует или перемещает пару ключ-значение в контейнер. Он перегружен, чтобы принимать ссылку-на-const или ссылку 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
позволяет избежать ненужных копий или движений, создавая элемент на месте. Он использует совершенную переадресацию и вариационный шаблон для форвардных аргументов конструктору пары ключ-значение:
template<class... Args>
std::pair<iterator,bool> emplace(Args&&... args);
Но между этими двумя функциями существует много перекрытий. emplace
можно использовать для пересылки конструктору copy/move пары ключ-значение, которая позволяет использовать его так же, как insert
. Это означает, что использование emplace
не гарантирует, что вы избежите копирования или перемещения. Кроме того, версия insert
, которая принимает rvalue-reference, фактически шаблонизирована и принимает любой тип P
, так что пара ключ-значение конструктивна из P
.
В принципе, функции размещения иногда должны быть более эффективными чем их вставные аналоги, и они никогда не должны быть меньше эффективным.
( Изменить: Говард Хиннант провел несколько экспериментов, которые иногда показывали insert
быстрее, чем emplace
).
Если вы действительно хотите скопировать/переместить в контейнер, может быть разумно использовать insert
, потому что вы, скорее всего, получите ошибку компиляции, если вы передадите неправильные аргументы. Вы должны быть более осторожны, вы передаете правильные аргументы функциям размещения.
Большинство реализаций unordered_map::emplace
приведет к динамическому распределению памяти для новой пары, даже если карта уже содержит элемент с этим ключом, а emplace
завершится с ошибкой. Это означает, что если есть вероятность, что сбой emplace
, вы можете получить лучшую производительность с помощью вставки, чтобы избежать ненужных распределений динамической памяти.
Небольшой пример:
#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: По запросу. Также возможно использовать unordered_map::emplace
с ключом или значением, которое принимает более одного параметра конструктора. Используя std::pair
кусочный конструктор, вы все равно можете избежать ненужных копий или перемещений.
#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
}