В чем разница между std::unordered_map::emplace
и std::unordered_map::insert
в C ++?
В чем разница между unordered_map :: emplace и unordered_map :: insert в C ++?
Ответы (2)
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
можно использовать для перенаправления в конструктор копирования / перемещения пары "ключ-значение", что позволяет использовать его так же, как insert
. Это означает, что использование emplace
не гарантирует, что вы избежите копий или перемещений. Кроме того, версия insert
, которая принимает rvalue-ссылку, фактически является шаблоном и принимает любой тип 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
}
emplace
для ассоциативных контейнеров обычно создает элемент (а не только ключ) из аргументов, даже если он не вставлен. Поэтому, если у вас уже есть элемент (вне ассоциативного контейнера) и вы хотите его скопировать, insert
может быть более эффективным.
- person dyp; 19.10.2014
pair
, которую мы можем завершить за второй проход. И то же самое, наверное, для tuple
s.
- person Yakk - Adam Nevraumont; 19.10.2014
T2
, который построен с более чем одним аргументом.
- person Chris Drew; 31.05.2016
Разница между emplace()
и _ 2_ уже хорошо объяснен в Ответ Криса Дрю. Однако для полноты картины я хотел бы добавить, что, поскольку C ++ 17 std::unordered_map
предоставляет два новых метода вставки: _ 4_ и _ 5_. Позвольте мне кратко резюмировать эти методы:
try_emplace()
- это улучшенная версияemplace()
. В отличие отemplace()
,try_emplace()
не изменяет свои аргументы (из-за операций перемещения), если вставка не удалась из-за ключа, уже существующего вunordered_map
.insert_or_assign()
- это улучшенная версияoperator[]
. В отличие отoperator[]
,insert_or_assign()
не требует, чтобы тип значенияunordered_map
был конструктивным по умолчанию.
Я написал более подробный ответ на упомянутые выше новые методы вставки std::map
здесь. Этот ответ также относится к std::unordered_map
.
std::pair<const Key, T>
) вызывается с точно такими же аргументами, которые были переданы в emplace, пересылаемые черезstd::forward<Args>(args)...
. - person chris   schedule 19.10.2014