連想コンテナ
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
は挿入した要素へのイテレータ、inserted
はtrue
、node
は空になります。
挿入に使用したノードが空だった場合は挿入に失敗し、position
はコンテナの終端(end
関数が返す値と同じ)、inserted
はfalse
、node
は空になります。
挿入に使用したノードのキーがコンテナ内に既に存在する場合も挿入に失敗し、position
はコンテナ内の既存の要素へのイテレータ、inserted
はfalse
、node
は挿入に使用したノードです。
位置ヒントを使用してノードを挿入した場合、戻り値は挿入した要素、あるいは既存の要素へのイテレータです。
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
このオーバーロードの戻り値は挿入した要素、またはコンテナ内の既存の要素へのイテレータです。