C++のstd::unordered_map::emplace
との違いは何ですか?std::unordered_map::insert
2 に答える
unordered_map::insert
キーと値のペアをコンテナーにコピーまたは移動します。const への参照または右辺値参照を受け入れるようにオーバーロードされます。
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);
しかし、2 つの機能には多くの重複があります。emplace
キーと値のペアのコピー/移動コンストラクターに転送するために使用できます。これにより、キーと値のペアをそのまま使用できますinsert
。これは、 を使用しemplace
ても、コピーや移動を回避できるとは限らないことを意味します。また、右辺値参照を取る のバージョンはinsert
、実際にはテンプレート化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
}