🥧
コンテナに効率よくデータを追加するemplace()
std::mapやstd::vectorのようなコンテナに要素を追加する際、普通はinsert()やpush_back()を使うと思いますが、emplace()やemplace_back()を使ったことありますか?
#include <string>
struct Person
{
std::string name;
std::string country;
int age;
};
std::vectorに上記の構造体の新しいインスタンスを追加したいとしましょう。
#include <vector>
std::vector<Person> people;
people.insert(people.begin(), {"Yamada Taro", "Japan", 30});
people.push_back({"Yamada Taro", "Japan", 30});
// vs.
people.emplace(people.begin(), "Yamada Taro", "Japan", 30);
people.emplace_back("Yamada Taro", "Japan", 30);
-
insert()やpush_back()の場合は、まずはPersonのインスタンスを作ってから渡すため、そのインスタンスがコンテナにコピーかムーブされます。 -
emplace()やemplace_back()の場合は、Personのコンストラクタに渡したい引数を指定すると、インスタンスを直接コンテナの中で作ってくれるため、コピーやムーブは不要になります。
もちろんパフォーマンス的にemplace()やemplace_back()の方がいいですね。
パフォーマンスと関係ないユースケースも紹介しましょう。
例えばPersonを以下のようにコピーとムーブ出来ないようにしてからstd::mapに追加したい場合は、insertはコピーかムーブが必要になるせいで使えなくなり、そのコンテナの中で直接作るemplace()を使わないといけなくなります。
#include <map>
struct Person
{
std::string name;
std::string country;
int age;
Person(std::string _name, std::string _country, int _age)
: name(_name), country(_country), age(_age)
{};
Person(const Person&) = delete; // コピーコンストラクタを禁止する
Person(Person&&) = delete; // ムーブコンストラクタを禁止する
Person& operator=(const Person&) = delete; // コピー代入演算子を禁止する
Person& operator=(Person&&) = delete; // ムーブ代入演算子を禁止する
};
std::map<std::string, Person> people;
people.insert({"yamataro", {"Yamada Taro", "Japan", 30}}); // コンパイルエラー
people.emplace(std::piecewise_construct,
std::forward_as_tuple("yamataro"),
std::forward_as_tuple("Yamada Taro", "Japan", 30)); // OK
是非いつか使ってみてください。
Discussion