連想コンテナ

STLコンテナ型その2

連想コンテナはSTLコンテナの一種です。
以下のコンテナクラスが連想コンテナに分類されます。

コンテナ名 説明 ヘッダファイル
set 集合
要素の重複不可
<set>
multiset 集合
要素の重複可
<set>
map 連想配列
要素の重複不可
<map>
multimap 連想配列
要素の重複可
<map>

連想コンテナは「キー」で要素を管理するのが特徴です。
set/multisetは要素自身がキーとなり、map/multimapは「キーと値」がペアとなる要素を持ちます。

連想コンテナは要素の並び順をプログラマが制御できません。
要素の並びは各コンテナクラスが制御していて、効率的な処理のために自動的に順序付けを行います。

ランダムアクセスはできません。
要素の挿入/削除は中速です。
要素の検索は中速です。

ランダムアクセスができないというのは、イテレータにit[3](it + 3)などの演算ができないということです。
途中の要素へのアクセスは、先頭または末尾の要素から順にたどっていくしかありません。
ただ、連想コンテナは「先頭から○番目の要素にアクセス」という操作が必要になることはほとんどありません。
もしこのような操作が必要ならばシーケンスコンテナを使用したほうが良いでしょう。

std::advance関数等を使用することで任意の位置の要素アクセスは可能ですが、これらの関数はイテレータがサポートしない移動が可能になるわけではありません。
つまり連想コンテナクラスに対するstd::advance関数の操作は、イテレータを指定の回数インクリメント/デクリメントするだけで速度は変わりません。

set/multisetコンテナ

setはキーが重複しない連想コンテナです。
例えば文字列中に出現する文字種を調べる場合に利用できます。

multisetはキーの重複を許可する連想コンテナです。
例えば文字列中に出現する特定の文字の数を数える場合に利用できます。


#include <iostream>
#include <string>
#include <set>

int main()
{
    std::string str = "cat, dog, rabbit, goat, sheep";
    std::cout << str << std::endl;

    {
        std::set<char> set;

        //strの中身をすべて追加
		//重複要素は自動的に除かれる
        set.insert(str.begin(), str.end());

        std::cout << "出現する文字: ";
        for (const auto& x : set) {
            std::cout << x << " ";
        }
    }

    std::cout << std::endl;

    {
        std::multiset<char> mset;

        //strの中身をすべて追加
        mset.insert(str.begin(), str.end());

        std::cout << "','の出現回数: ";
        std::cout << mset.count(',');
    }

    std::cin.get();
}
cat, dog, rabbit, goat, sheep
出現する文字:  , a b c d e g h i o p r s t
','の出現回数: 4

イテレータは読み取り専用

set/multisetでもイテレータを使用して要素アクセスが可能ですが、非constなset/multisetであってもイテレータを通して値を書き換えることはできません。
これらのクラスの「値」は「キー」であり、キーは順序を決定するために使用されるので、キーを書き換えると順序がバラバラになってしまうためです。
値の書き換えはいったん要素を削除してから再挿入という形で行います。

map/multimapコンテナ

map/multimapは「キー」と「値」のペアを要素とするコンテナです。
mapはキーの重複を許可しません。
multimapはキーの重複が可能です。

std::pairクラス

map/multimapの要素はstd::pairというクラス(クラステンプレート)のオブジェクトです。
このクラスは二つの異なるデータ型をペアとして扱えます。


//string型とint型のペア
std::pair<std::string, int> pair = { "abc", 123 };

//abc
std::string key = pair.first;
//123
int value = pair.second;

//C++03ではstd::make_pair関数を使用する
pair = std::make_pair("def", 456);

std::pairは波括弧{}に指定のデータ型の値を指定することでインスタンスを初期化できます。
(これ自体は他のクラスでも使用可能で、C++11から導入された記法です)
最初の要素はfirstメンバ変数、二番目の要素はsecondメンバ変数に格納されます。
map/multimapではfirstメンバがキー、secondメンバが値となります。

なお、std::pairクラスは<utility>というヘッダファイルに定義されています。

C++17からは、構造化束縛で要素を取り出すこともできます。


std::pair<std::string, int> pair = { "abc", 123 };

//std::pairのそれぞれの要素から型推論して
//変数keyとvalueを宣言して代入
auto [key, value] = pair;
std::cout << key << " : " << value << std::endl;
//abc : 123

//mapの各要素はstd::pair型
std::map<std::string, int> map = {
	{ "cat", 1 },
	{ "dog", 2 },
	{ "rabbit", 3 },
	{ "goat", 1 }
};

//範囲for文でも使用できる
//constや参照(&)も可能
for (const auto& [key, value] : map) {
	std::cout << key << " : " << value << std::endl;
}
//cat : 1
//dog : 2
//goat : 1
//rabbit : 3

mapの要素アクセス

配列やvectorクラスは先頭からの要素番号(数字)で要素にアクセスしますが、mapは任意のデータ型をキーにして値にアクセスすることができます。


#include <iostream>
#include <string>
#include <map>

int main()
{
	//キーが重複する場合は除かれる
	std::map<std::string, int> map = {
		{ "cat", 1 },
		{ "dog", 2 },
		{ "rabbit", 3 },
		{ "goat", 1 },
		{ "rabbit", 5 } //重複
	};

	//[]演算子にキーを指定して値にアクセスできる
	int cat = map["cat"];
	map["dog"] = 9;

	//存在しないキーを指定するとデフォルト値で要素が作成される
	int fox = map["fox"];

	for (const auto& x : map) {
		std::cout << x.first;
		std::cout << " : ";
		std::cout << x.second;
		std::cout << std::endl;
	}
	//cat : 1
	//dog : 9
	//fox : 0
	//goat : 1
	//rabbit : 3
}

配列と同じように添字演算子([]演算子)に目的のキーを指定することで、要素の値にアクセスすることができます。
これはランダムアクセスに見えますが、処理としては要素の検索なので「中速」な計算時間となります。

存在しないキーに対して値を挿入して新しい要素を作成できます。

ただし、添字演算子が使用できるのは非constなmapだけです。
constなmapの要素アクセスはat関数を使用します。


const std::map<std::string, int> map = {
	{ "cat", 1 }
};

//constなmapでは[]演算子は使用できない
//以下はどちらもコンパイルエラー
//int cat = map["cat"];
//map["cat"] = 9;

//at関数はOK
int cat = map.at("cat");

//constなので書き換えは不可
//map.at("cat") = 9;

//at関数の場合は
//存在しないキーにアクセスすると例外発生
//(out_of_range)
//int fox = map.at("fox");

multimapはひとつのキーに対して複数の値が格納可能ですから、[]演算子やat関数で特定の要素にアクセスすることはできません。

キーは読み取り専用

map/multimapでもイテレータ使用して要素アクセスが可能ですが、キーを書き換えることはできません。
キーの書き換えはいったん要素を削除してから再挿入という形で行います。

自作クラスをキーにする

連想コンテナのキーには数値や文字列など色々な型を指定できますが、要素の並び順の決定にはstd::lessを使用します。
自作クラスのインスタンスをキーに指定する場合は<演算子をオーバーロードしておく必要があります。


#include <map>

class TestClass
{
	int n;

public:
	TestClass(int n = 0)
		:n(n) {}

	bool operator <(const TestClass& r) const
	{
		return n < r.n;
	}
};

int main()
{
	std::map<TestClass, int> map{
		{TestClass(1), 1},
		{TestClass(9), 2},
		{TestClass(5), 3},
	};
}

なお、set/multisetのテンプレート第二引数、map/multimapのテンプレート第三引数には並べ替えを行う関数オブジェクトを指定できます。
デフォルトではここにstd::lessが指定されているわけですが、これを変更した場合はその関数オブジェクトが使用する演算子のオーバーロードが必要です。
例えばstd::greaterに変更した場合は>演算子のオーバーロードが必要です。


class TestClass
{
	int n;

public:
	TestClass(int n = 0)
		:n(n) {}

	//std::less用
	bool operator <(const TestClass& r) const
	{
		return n < r.n;
	}

	//std::greater用
	bool operator >(const TestClass& r) const
	{
		return n > r.n;
	}
};

int main()
{
	std::set<TestClass, std::greater<TestClass>> set{
		TestClass(1),
		TestClass(9),
		TestClass(5)
	};

	std::map<TestClass, int, std::greater<TestClass>> map{
		{TestClass(1), 1},
		{TestClass(9), 2},
		{TestClass(5), 3},
	};
}

共通のメンバ関数

連想コンテナで共通するメンバ関数の対応一覧です。
一覧にない、コンテナクラス固有のメンバ関数は後述します。

   set  multiset  map  multimap 説明
イテレータ
begin 先頭要素のイテレータ取得
cbegin 先頭要素のconstイテレータ取得
(C++11)
end 末尾要素の次のイテレータ取得
cend 末尾要素の次のconstイテレータ取得
(C++11)
rbegin 末尾要素の逆イテレータ取得
crbegin 末尾要素のconst逆イテレータ取得
(C++11)
rend 先頭要素の手前の逆イテレータ取得
crend 先頭要素の手前のconst逆イテレータ取得
(C++11)
要素アクセス
at
operator[]
× × × 任意の位置の要素へのアクセス
容量
empty コンテナが空か否かの判定
size 要素数の取得
max_size システムが格納可能な最大の要素数を取得
コンテナの変更
clear 全ての要素を削除
insert 要素の挿入
emplace 要素を構築して挿入
emplace_hint 挿入位置ヒントを与え、要素を構築して挿入
erase 要素を削除
swap コンテナオブジェクトの入れ替え
merge コンテナの結合
(C++17以降)
extract 要素の切り離し
(C++17以降)
検索
count キーに一致する要素数を取得
find キーに一致するイテレータを取得
contains キーに一致する要素が含まれるかを判定
(C++20以降)
lower_bound キー以下の最後の要素のイテレータを取得
upper_bound キーより大きい最初の要素のイテレータを取得
equal_range キーに一致する範囲のイテレータをstd::pairで取得
関数オブジェクト
key_comp キーの比較オブジェクトを取得
value_comp 要素の比較オブジェクトを取得
アロケーター
get_allocator アロケーターオブジェクトを取得
   set  multiset  map  multimap 説明

arrayクラスvectorクラスと共通の関数は当該ページを参照してください。
イテレータについてはイテレータの項を参照してください。
以下は、上記のページでは説明していない関数について説明します。

以下のサンプルコードでは戻り値のデータ型を明示的に記述しているものもありますが、やたらと長いデータ型名が多いのでauto(型推論)を使用することをお勧めします。

decltypeはコンテナ型やデータ型に依存しない記述ができるので、autoが使用できない場面ではこちらが便利です。


std::vector<int> vec;

//全て同じデータ型
std::vector<int>::iterator it1;
auto it2 = vec.begin();
decltype(vec)::iterator it3;

insert、emplace関数

insert関数、emplace関数は、コンテナに要素を追加します。
基本的にvectorクラスなどのinsert関数emplace関数と同じですが、微妙に異なるので改めて説明します。

単純な挿入

連想コンテナクラスの要素は自動的に順序付けされるため、挿入位置の指定は必要ありません。


std::set<int> set = { 1, 5, 8 };

//単純な挿入
//挿入位置は自動的に決定される
std::pair<std::set<int>::iterator, bool> pair1 = set.insert(3);
//1 3 5 8

//戻り値はstd::pair<std::set<int>::iterator, bool>型
//記述が面倒なのでautoで受け取るのが良い

//mapの場合
std::map<std::string, int> map = {
	{ "abc", 1 },
	{ "def", 2 }
};
std::pair<std::map<std::string, int>::iterator, bool> pair2 = map.insert({ "ghi", 3 });

emplace関数はこの単純な挿入のみが可能で、後述する位置ヒント指定、複数要素の同時の挿入、ノードの挿入はできません。

set/map

set/mapでは、戻り値はstd::pair<std::コンテナクラス<データ型>::iterator, bool>というstd::pair型です。
この型のfirstメンバは挿入した要素へのイテレータ、secondメンバは挿入の成否を表わすbool値です。
すでに同じキーが存在していた場合はsecondメンバはfalseになります。
その場合でもfirstメンバのイテレータは有効で、コンテナ内の既存の要素を指します。

multiset/multimap

multiset/multimapはキーの重複を許可するので、挿入は常に成功します。
つまり成否判定は必要ないので、戻り値は挿入した要素へのイテレータです。

位置ヒントを指定して挿入(insert関数)

insert関数は、挿入位置のヒントを与えた上で挿入することができます。
ヒントはイテレータで指定します。


std::set<int> set = { 1, 5, 8 };
std::pair<std::set<int>::iterator, bool> pair = set.insert(3);

//位置ヒントを指定して挿入
std::set<int>::iterator it = set.insert(pair.first, 2);
//1 2 3 5 8
//戻り値は挿入した要素へのイテレータ

位置ヒントが適切である場合は高速な挿入が可能です。
間違っていた場合は従来通り「中速」な挿入となります。

連想コンテナの要素は自動的にソートされるため、指定するのはあくまでも位置ヒントです。
指定の位置への挿入を強制するものではありません。
位置ヒントは挿入要素の適切な位置をプログラマが把握できる場合に有効です。

この場合の戻り値は挿入した要素、あるいは既存の要素を指すイテレータです。

イテレータ範囲/初期化リストから挿入(insert関数)

insert関数は、別のコンテナオブジェクトのイテレータから範囲を指定して挿入することが出来ます。
また、初期化リストを指定して挿入することもできます。
vectorクラス等のinsert関数でも同様の操作(関数オーバーロード)が存在します。


std::set set = { 1, 5, 8 };
std::vector vec = { 3, 6, 9 };

//イテレータから範囲指定して挿入
set.insert(vec.begin(), vec.end());
//1 3 5 6 8 9

//初期化リストを挿入
set.insert({ 6, 7, 8 });
//1 3 5 6 7 8 9

これらの挿入では戻り値はありません。

ノードを挿入(insert関数)

insert関数はノードというものを挿入することができます。
これは他のコンテナから抽出した要素のことです。
これについてはextract関数で説明します。

emplace_hint関数

emplace_hint関数は、位置ヒントを使用して、要素を構築して挿入します。
位置ヒントが使用できることと、戻り値が異なる以外はemplace関数と同じです。
位置ヒントに関してはinsert関数を参照してください。


std::set<int> set = { 1, 5, 8 };

//位置ヒントを指定して、要素を構築して挿入
std::set<int>::iterator it = set.emplace_hint(set.begin(), 0);
//0 1 5 8

戻り値は挿入した要素、あるいは既存の要素を指すイテレータです。

merge関数

merge関数は、別の連想コンテナのオブジェクトから要素を抽出し、呼び出し元のオブジェクトに結合します。
この関数はC++17から使用できます。


std::set<int> set1 = { 1, 2 };
std::set<int> set2 = { 2, 4, 6 };

//set2からset1に存在しない要素を抽出して結合
set1.merge(set2);

set1;
//1 2 4 6

set2;
//2

抽出元のコンテナの要素は移動されます。
(内部的にはポインタのすげ替えです)
setに連結する場合、すでに存在する要素は移動されずに抽出元のコンテナに残ります。

この関数の戻り値はありません。

なお、setとmultiset、mapとmultimapの同じデータ型を管理するオブジェクトはそれぞれ互換性があります。

extract関数

extract関数は、コンテナクラスのオブジェクトから要素を抽出します。
この関数はC++17から使用できます。


std::set<int> set = { 1, 2, 3, 4, 5 };

//ノード(要素)を抽出
std::set<int>::node_type nh1 = set.extract(1);
std::set<int>::node_type nh2 = set.extract(3);

//存在しない要素を指定した場合は空ノード
std::set<int>::node_type nh3 = set.extract(99);

if(!nh1.empty()) //空でなければ実行
	std::cout << nh1.value() << std::endl;
if(nh2) //空でなければ実行
	std::cout << nh2.value() << std::endl;
if(!nh3.empty()) //nh3は空ノードなのでこれは実行されない
	std::cout << nh3.value() << std::endl;

std::set<int>::iterator it = set.begin();
while (it != set.end())
{
	std::cout << *it++ << " ";
}
1
3
2 4 5

node_type型

戻り値はstd::コンテナクラス<データ型>::node_type型です。
これはノードハンドルというもので、連想コンテナクラス、および非順序連想コンテナクラスの要素を管理するための型です。
extract関数で抽出した時点で要素はノードハンドル側に移動し、元のコンテナクラスからは削除されます。

set/multisetクラスの場合はvalueというメンバ関数で管理する要素にアクセスできます。
(読み取り/書き込みの両方が可能)
map/multimapクラスの場合はkeyメンバ関数でキーに、mappedメンバ関数で値にアクセスできます。

指定のキーが存在しない場合は空のノードハンドルが返されます。
ノードハンドルが空か否かはempty関数や、条件判定でチェックできます。

空のノードハンドルを作ることはできますが、値の読み書きはできません。
ノードハンドルはムーブオンリーな型で、extract関数でコンテナクラスから要素を抽出するか、他のノードハンドルからムーブして要素の所有権を得る必要があります。
ノードをゼロから作成することや、コピーの作成はできません。


std::set<int> set = { 1, 2, 3 };
std::multiset<int> mset;

std::set<int>::node_type nh1 = set.extract(1);
std::multiset<int>::node_type nh2; //空のノードハンドル

//ノードハンドルの値の書き換え
nh1.value() = 4;

//空のノードハンドルへの書き込みはアクセス違反
//nh2.value() = 5;

//コピーはできない
//nh2 = nh1;

//ノードの所有権の移動
//setとmultisetのノードは互換性がある
nh2 = std::move(nh1);

//OK
nh2.value() = 5;

//移動元のノードハンドルは空になるのでNG
//nh1.value() = 6;

insert関数

抽出した要素はinsert関数でコンテナに挿入します。
所有権の移動なのでstd::moveが必要です。
(ムーブについてはムーブセマンティクスを参照してください。)


std::set<int> set = { 1, 2, 3 };
std::multiset<int> mset;

std::set<int>::node_type nh1 = set.extract(1);
std::set<int>::node_type nh2 = set.extract(2);

//setとmultisetのノードは互換性がある
std::multiset<int>::iterator itM =  mset.insert(std::move(nh1));

//位置ヒントを使用して挿入
mset.insert(++itM, std::move(nh2));
set/map

set/mapコンテナにおけるノードの単純な挿入では、戻り値はstd::コンテナクラス<データ型>::insert_return_type型です。
insert_return_type型は以下のメンバを持ちます。

データ型 メンバ名 説明
Iterator position 挿入した要素、あるいは既存要素へのイテレータ
bool inserted 挿入の成否を示す真偽値
NodeType node 挿入に使用したノード

挿入に成功した場合はpositionは挿入した要素へのイテレータ、insertedtruenodeは空になります。

挿入に使用したノードが空だった場合は挿入に失敗し、positionはコンテナの終端(end関数が返す値と同じ)、insertedfalsenodeは空になります。

挿入に使用したノードのキーがコンテナ内に既に存在する場合も挿入に失敗し、positionはコンテナ内の既存の要素へのイテレータ、insertedfalsenodeは挿入に使用したノードです。

位置ヒントを使用してノードを挿入した場合、戻り値は挿入した要素、あるいは既存の要素へのイテレータです。

multiset/multimap

multiset/multimapコンテナでのノードの挿入は、挿入した要素へのイテレータです。
ノードが空だった場合はコンテナ終端へのイテレータです。

count関数

count関数は、コンテナ内にある指定のキーの数を数えます。


{
	std::set<int> set = { 1, 2, 3, 1 };

	std::set<int>::size_type count1 = set.count(1);
	std::set<int>::size_type count3 = set.count(3);
	std::set<int>::size_type count5 = set.count(5);
	//1
	//1
	//0
}
{
	std::multiset<int> mset = { 1, 2, 3, 1 };

	std::multiset<int>::size_type count1 = mset.count(1);
	std::multiset<int>::size_type count3 = mset.count(3);
	std::multiset<int>::size_type count5 = mset.count(5);
	//2
	//1
	//0
}

setはキーの重複を許可しないので、0か1のみが返ってきます。
戻り値はstd::コンテナクラス<データ型>::size_typeという型ですが、これはほとんどの場合でstd::size_t型の別名です。
(実装次第です)

find関数

find関数は、コンテナ内にある指定のキーを指すイテレータを取得します。


std::set<int> set = { 1, 2, 3 };

std::set<int>::iterator it = set.find(2);
if (it != set.end()) {
	std::cout << "見つかった" << std::endl;
}
else {
	std::cout << "見つからなかった" << std::endl;
}

キーが見つからなかった場合はコンテナの終端を示すイテレータ(end())を返します。
multisetやmultimapでは最初に見つかった要素へのイテレータを返します。

contains関数

contains関数は、コンテナ内に指定のキーが存在するかを判定します。
この関数はC++20から使用できます。


std::set<int> set = { 1, 2, 3 };

bool b1 = set.contains(1);
//true
bool b5 = set.contains(5);
//false

count関数でも存在のチェックは可能ですが、contains関数はキーが見つかった時点で検索を終了するので高速に動作する可能性があります。

lower_bound関数

upper_bound関数

lower_bound関数は、指定のキーの位置未満が成立しなくなる最初の要素へのイテレータを取得します。
upper_bound関数は、指定のキーの位置よりも大きい最初の要素へのイテレータを取得します。


std::set<int> set = { 1, 2, 3, 4, 5 };

std::set<int>::iterator lower = set.lower_bound(3);
//3
std::set<int>::iterator upper = set.upper_bound(3);
//4

lower_bound関数は、言い換えれば引数を右辺として>=による比較が最初に成立する要素へのイテレータを取得します。
upper_bound関数は、>による比較が最初に成立する要素へのイテレータを取得します。
比較が成立するキーが存在しない場合はコンテナの終端を示すイテレータ(end())を返します。

equal_range関数

equal_range関数は、指定のキーに一致するイテレータの範囲を取得します。


{
	std::set<int> set = { 1, 2, 3, 4, 5 };

	std::pair<std::set<int>::iterator, std::set<int>::iterator> equals = set.equal_range(3);

	equals.first;
	//"3"を示すイテレータ
	equals.second;
	//"3"の次を示すイテレータ
}
{
	std::multimap<std::string, int> mmap = { 
		{"a", 1},
		{"b", 2},
		{"a", 3},
		{"b", 4},
		{"c", 5},
	};

	std::pair<std::multimap<std::string, int>::iterator, std::multimap<std::string, int>::iterator>
		equals = mmap.equal_range("b");

	equals.first;
	//{"b", 2}を示すイテレータ
	equals.second;
	//{"b", 4}の次を示すイテレータ

	while (equals.first != equals.second) {
		std::cout << equals.first->first << " ";
		std::cout << equals.first->second << std::endl;
		equals.first++;
	}
	//b 2
	//b 4
}

戻り値はイテレータのペア(std::pair)です。
firstメンバには一致する最初の要素へのイテレータ、secondメンバには一致する最後の要素の次の要素へのイテレータが格納されます。

一致するキーが存在しない場合、firstメンバおよびsecondメンバには指定したキーよりも大きい最初のキーを指すイテレータが格納されます。
(両方のイテレータは一致します)
そのようなキーが存在しない場合はコンテナの終端を示すイテレータ(end())が格納されます。

set/mapはキーの重複が許可されないため、イテレータの範囲はひとつの要素、またはゼロです。

key_comp関数

value_comp関数

key_comp関数は、コンテナに関連付けられた、キーの比較オブジェクトを取得します。
value_comp関数は、コンテナに関連付けられた、要素の比較オブジェクトを取得します。


{
	std::set<int> set;
	std::set<int>::key_compare key_comp = set.key_comp();

	bool b1 = key_comp(1, 2);
	bool b2 = key_comp(5, 5);
	bool b3 = key_comp(9, 8);
	//true
	//false
	//false
}
{
	std::map<std::string, int> map;
	std::map<std::string, int>::key_compare key_comp = map.key_comp();
	std::map<std::string, int>::value_compare value_comp = map.value_comp();

	bool b1 = key_comp("a", "b");
	bool b2 = key_comp("c", "c");
	bool b3 = key_comp("z", "y");
	//true
	//false
	//false

	bool b4 = value_comp({ "a", 6 }, { "b", 5 });
	bool b5 = value_comp({ "c", 4 }, { "c", 3 });
	bool b6 = value_comp({ "z", 2 }, { "y", 1 });
	//true
	//false
	//false
}

key_comp関数は、キーの順序を判定する関数オブジェクトを取得します。
この関数は第一引数が第二引数よりも小さい場合はtrueを返します。
そうでない場合はfalseを返します。
(一致する場合でもfalseです)

value_comp関数は、要素の順序を判定する関数オブジェクトを取得します。
要素の順序判定は要素のキーによるので、判定基準はkey_comp関数と同じです。

key_comp関数は「キー」そのものを引数に指定するのに対して、value_comp関数は「要素」を引数に指定する点が異なります。
set/multisetでは「キー=要素」なのでどちらも同じ関数オブジェクトが使用されます。

mapクラスのメンバ関数

mapクラスには上記以外にもメンバ関数が存在します。

insert_or_assign関数

insert_or_assign関数は、指定のキーが存在しない場合は要素を挿入し、存在する場合は要素の値を書き換えます。
この関数はC++17から使用できます。


std::map<std::string, int> map = {
	{ "a", 1 },
	{ "b", 2 }
};

std::pair<std::map<std::string, int>::iterator, bool> result1
	= map.insert_or_assign("a", 5);
std::pair<std::map<std::string, int>::iterator, bool> result2
	= map.insert_or_assign("c", 6);

std::cout << "(" << result1.first->first << ", " << result1.first->second << "), " << result1.second << std::endl;
//(a, 5), 0
std::cout << "(" << result2.first->first << ", " << result2.first->second << "), " << result2.second << std::endl;
//(c, 6), 1

for (const auto& x : map) {
	std::cout << x.first << " " << x.second << std::endl;
}
//a 5
//b 2
//c 6

//戻り値はstd::pair型で、firstメンバはイテレータ
//そのイテレータが指す要素もstd::pair型
//つまり
//「result1.first->first」はキー
//「result1.first->second」は値
//を表す
(a, 5), 0
(c, 6), 1
a 5
b 2
c 6

戻り値はstd::pair<std::map<データ型>::iterator, bool>型です。
pairのfirstメンバは挿入した要素、または既存の要素へのイテレータです。
secondメンバは挿入の成否で、要素が挿入された場合はtrue、指定のキーがすでに存在した場合はfalseが格納されます。

位置ヒントを指定して挿入

insert_or_assign関数は、第一引数に位置ヒントを指定して挿入/置き換えが可能です。
位置ヒントに関してはinsert関数を参照してください。


std::map<std::string, int> map = {
	{ "a", 1 },
	{ "b", 2 }
};

std::map<std::string, int>::iterator it1
	= map.insert_or_assign(map.begin(), "a", 5);
std::map<std::string, int>::iterator it2
	= map.insert_or_assign(map.end(), "c", 6);

for (const auto& x : map) {
	std::cout << x.first << " " << x.second << std::endl;
}
//a 5
//b 2
//c 6

このオーバーロードの戻り値は挿入または置き換えをした要素へのイテレータです。

try_emplace関数

try_emplace関数は、指定のキーが存在しない場合は要素を構築して挿入します。
キーが既に存在する場合は何もしません。
この関数はC++17から使用できます。


std::map<std::string, int> map = {
	{ "a", 1 },
	{ "b", 2 }
};

std::pair<std::map<std::string, int>::iterator, bool> result1
	= map.try_emplace("a", 5);
std::pair<std::map<std::string, int>::iterator, bool> result2
	= map.try_emplace("c", 6);

std::cout << "(" << result1.first->first << ", " << result1.first->second << "), " << result1.second << std::endl;
//(a, 1), 0
std::cout << "(" << result2.first->first << ", " << result2.first->second << "), " << result2.second << std::endl;
//(c, 6), 1

for (const auto& x : map) {
	std::cout << x.first << " " << x.second << std::endl;
}
//a 1
//b 2
//c 6
(a, 1), 0
(c, 6), 1
a 1
b 2
c 6

戻り値はstd::pair<std::map<データ型>::iterator, bool>型です。
pairのfirstメンバは挿入した要素、または既存の要素へのイテレータです。
secondメンバは挿入の成否で、要素が挿入された場合はtrue、指定のキーがすでに存在した場合はfalseが格納されます。

位置ヒントを指定して挿入

try_emplace関数は、第一引数に位置ヒントを指定して挿入が可能です。
位置ヒントに関してはinsert関数を参照してください。


std::map<std::string, int> map = {
	{ "a", 1 },
	{ "b", 2 }
};

std::map<std::string, int>::iterator it1
	= map.try_emplace(map.begin(), "a", 5);
std::map<std::string, int>::iterator it2
	= map.try_emplace(map.end(), "c", 6);

for (const auto& x : map) {
	std::cout << x.first << " " << x.second << std::endl;
}
//a 1
//b 2
//c 6

このオーバーロードの戻り値は挿入した要素、またはコンテナ内の既存の要素へのイテレータです。