web-dev-qa-db-ja.com

unordered_map :: emplaceとunordered_map :: insert in C ++の違いは何ですか?

違いは何ですか std::unordered_map::emplaceおよびstd::unordered_map::insert C++で?

23
Harsh M. Shah

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を受け入れます。

スコット・マイヤーズは言う:

原則として、埋め込み関数は挿入関数よりも効率的である場合があり、効率が低下することはありません。

Edit:Howard Hinnant ran 一部の実験 時折insertemplaceよりも速いことを示した)

間違いなくコンテナにコピー/移動したい場合は、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::pairpiecewise constructor を使用すると、不要なコピーや移動を回避できます。

#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
}
41
Chris Drew