🥧

コンテナに効率よくデータを追加するemplace()

2024/10/20に公開

std::mapstd::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

是非いつか使ってみてください。


|cpp記事一覧へのリンク|

Discussion