最終更新日時:
が更新

履歴 編集

コンテナとは

概要

「コンテナ (container)」とは、C++標準ライブラリにおけるデータ構造の名称である。要素となるオブジェクトを保持し、その追加・参照・削除・列挙といった操作を提供する。どの操作を提供するかは、コンテナの種類によって異なる。

標準ライブラリのコンテナは、要素をどのように保持し、どのように目的の要素を見つけるかによって、シーケンスコンテナ・連想コンテナ・コンテナアダプタに分類される。どのコンテナも要素を所有し、コンテナを破棄すると保持している要素も破棄される。

なお、std::spanやstd::mdspanは、ほかの場所にある要素列を参照するだけで要素を所有しないため、コンテナではない。またstd::basic_stringは文字のシーケンスを保持し、シーケンスコンテナと同様に扱えるが、文字列を扱うクラスとして別に規定されている。

シーケンスコンテナ

「シーケンスコンテナ (sequence container)」は、要素を一列に並べて保持する。要素を追加する位置を指定でき、要素の並び順はその位置によって決まる。

名前 説明 対応バージョン
array 固定長の配列 C++11
vector 可変長の配列
inplace_vector 容量が固定された可変長の配列 C++26
deque 両端キュー
list 双方向リンクリスト
forward_list 単方向リンクリスト C++11
hive 要素のメモリ位置が安定したコンテナ C++26

ただしstd::arrayは要素数が固定されており、要素の追加・削除を行えない。std::hiveは挿入位置をコンテナが決定するため、要素の順序は未規定である。

連想コンテナ

「連想コンテナ (associative container)」は、キーから要素を探せるように要素を保持する。キーの比較によって順序付けて保持するものと、キーのハッシュ値によって分類して保持するもの (非順序連想コンテナ) がある。

名前 説明 対応バージョン
map 順序付き連想配列
multimap キーの重複を許す順序付き連想配列
set 順序付き集合
multiset 要素の重複を許す順序付き集合
unordered_map 非順序連想配列 C++11
unordered_multimap キーの重複を許す非順序連想配列 C++11
unordered_set 非順序集合 C++11
unordered_multiset 要素の重複を許す非順序集合 C++11

連想配列はキーと値の組を要素として保持し、集合はキーそのものを要素として保持する。

コンテナアダプタ

「コンテナアダプタ (container adaptor)」は、ほかのコンテナを内部に保持し、それを別のデータ構造として見せる。

名前 説明 対応バージョン
stack LIFOスタック
queue FIFOキュー
priority_queue 優先順位付きキュー
flat_map ソート済みのシーケンスによる順序付き連想配列 C++23
flat_multimap ソート済みのシーケンスによるキーの重複を許す順序付き連想配列 C++23
flat_set ソート済みのシーケンスによる順序付き集合 C++23
flat_multiset ソート済みのシーケンスによる要素の重複を許す順序付き集合 C++23

std::stack・std::queue・std::priority_queueは、提供する操作を限定することで用途を明確にする。そのため、これらのコンテナは要素を列挙する操作を提供しない。flat_で始まるコンテナは、ソート済みのシーケンスを内部に保持することで、連想コンテナと同じ操作を提供する。

例

ここでは、シーケンスコンテナと連想コンテナを例として、基本的な操作を示す。

要素を追加する

シーケンスコンテナは要素を追加する位置を指定し、連想配列はキーと値の組を追加する。どちらも、範囲for文で全要素を列挙できる。

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

int main()
{
  // シーケンスコンテナ : 末尾に要素を追加する
  std::vector<int> v;
  v.push_back(1);
  v.push_back(3);

  // シーケンスコンテナ : 位置を指定して要素を挿入する
  // ここでは、先頭から1要素進んだ位置に2を挿入する
  v.insert(v.begin() + 1, 2);

  for (int x : v) {
    std::cout << x << std::endl;
  }

  // 連想コンテナ : キーと値の組を追加する
  std::map<std::string, int> m;
  m.insert({"one", 1});
  m["two"] = 2;

  for (const auto& [key, value] : m) {
    std::cout << key << " : " << value << std::endl;
  }
}

出力

1
2
3
one : 1
two : 2

std::mapは、キーの順序で要素を保持するため、列挙する順序は追加した順序ではなくキーの昇順となる。

要素を参照する

要素を参照する方法は、コンテナがどのように要素を保持しているかによって異なる。

#include <iostream>
#include <iterator>
#include <vector>
#include <list>
#include <map>
#include <string>

int main()
{
  std::vector<int> v = {1, 2, 3};
  std::list<int> ls = {1, 2, 3};
  std::map<std::string, int> m = {{"one", 1}, {"two", 2}};

  // vectorは、位置を添字で指定して参照できる
  std::cout << v[1] << std::endl;

  // listは添字で指定できないため、イテレータを進めて位置を指定する
  std::cout << *std::next(ls.begin(), 1) << std::endl;

  // mapは、キーを指定して値を参照する
  std::cout << m.at("two") << std::endl;
}

出力

2
2
2

std::vectorは要素を連続した領域に並べて保持するため、どの位置の要素も定数時間で参照できる。std::listは要素どうしをリンクでつないで保持するため、先頭から順にイテレータを進める必要があり、位置に比例した時間がかかる。

要素を削除する

シーケンスコンテナは削除する要素の位置をイテレータで指定し、連想コンテナはキーを指定できる。

#include <iostream>
#include <iterator>
#include <vector>
#include <list>
#include <map>
#include <string>

int main()
{
  std::vector<int> v = {1, 2, 3};
  std::list<int> ls = {1, 2, 3};
  std::map<std::string, int> m = {{"one", 1}, {"two", 2}};

  // シーケンスコンテナは、削除する要素の位置をイテレータで指定する
  v.erase(v.begin() + 1);
  ls.erase(std::next(ls.begin(), 1));

  // 連想コンテナは、削除する要素のキーを指定できる
  m.erase("two");

  for (int x : v) {
    std::cout << x << std::endl;
  }
  for (int x : ls) {
    std::cout << x << std::endl;
  }
  for (const auto& [key, value] : m) {
    std::cout << key << " : " << value << std::endl;
  }
}

出力

1
3
1
3
one : 1

関連項目

  • イテレータとは
  • 各種コンテナの特徴比較