mapのemplace

C++11標準ライブラリのコンテナには、emplace()emplace_back()といったメンバ関数が追加されました。これらの関数は、コンテナの要素型のコンストラクタ引数を受け取り、要素型のオブジェクトをemplace()emplace_back()の内部で一度だけ作る、という効率化の役割を果たしてくれます。

std::vectoremplace_back()なら、以下のように、要素型Xコンストラクタ引数を渡すという、シンプルな使い方ができます。

struct X {
    X(int a, int b, int c) {}
};

std::vector<X> v;
v.emplace_back(1, 2, 3);

しかし、std::mapemplace()は、使うのがちょっと難しいです。mapはキーと値の組を扱うので、要素を追加する際、それぞれのコンストラクタ引数を受け取ろうとすると、引数の区切りがどこなのかがわからないのです。

std::map<X, Y> m;

// どこまでがXの引数で、どこからがYの引数?
m.emplace(1, 2, 3, 4, 5, 6);

引数の区切りを明確にするためには、それぞれの引数をタプルにパックして転送する必要があります。結果として、std::mapemplace()は以下のように使います。

#include <iostream>
#include <map>

struct X {
    int a, b, c;
    X(int a, int b, int c)
        : a(a), b(b), c(c) {}
};

bool operator<(const X& l, const X& r)
{
    return std::tie(l.a, l.b, l.c) < std::tie(r.a, r.b, r.c);
}

struct Y {
    Y(int d, int e, int f) {}
};

int main()
{
    std::map<X, Y> m;

    m.emplace(
        std::piecewise_construct,
        std::forward_as_tuple(1, 2, 3),  // Xのコンストラクタ引数
        std::forward_as_tuple(4, 5, 6)); // Yのコンストラクタ引数
}

std::forward_as_tuple()関数を使うと、tuple<T1&&, T2&&, T3&&...>のような、右辺値参照のタプルができます。

std::pairコンストラクタに、そのようなタプルを展開して各要素を構築するためのオーバーロードが用意されているので、それにおまかせすることになります。pairのこの構築方法については、以下のエントリを参照: