C++ Learning

第16回 std::array ― 型安全な固定長配列

C の int a[5] は便利ですが、関数に渡すとサイズ情報が失われる(ポインタに減衰する)、境界チェックがない、= で代入できない、などの罠がありました。std::array はこれら全部を解決した「サイズを持ち運ぶ安全な配列」です。vector と違いコンパイル時にサイズが決まるので、ヒープ確保もなし。

このページで押さえること
✅ 最低限ここだけ覚える
  • std::array<型, 個数> で固定長配列
  • サイズはコンパイル時に決まる
  • a.size() / a[i] は vector と同じ
  • 関数に渡してもサイズが保たれる
⭐ 余裕があれば読む
  • C 配列との比較(ポインタ減衰)
  • vector との使い分け
  • constexpr 文脈で使える
  • a.fill(v) / a.swap(b)

1. まず触ってみる ― std::array の作り方

std::array は、サイズが最初から決まっている配列です。vector のように途中で伸び縮みしません。その代わり、ヒープ確保がなく、C 配列並みに速い。しかも vector と同じメソッド(size(), [i], front(), back())が使えます。

▶ std::array<int, 5> を作って覗く
std::array<int, 5> a = { , , , , };

まずは 3 行だけ

first_array.cppC++ 最小例
#include <array> std::array<int, 5> a = {10, 20, 30, 40, 50}; a[0] = 99; // vector と同じ感覚 std::cout << a.size(); // 5
ここまでで覚えること(2 つだけ):
  • std::array<型, サイズ> の形で作る(サイズはコンパイル時に決まる数字)
  • 使い方は vector とほぼ同じa[i], a.size(), 範囲 for もそのまま)

よくある素朴な疑問

Q. vector があるのに、なぜ array が必要?
→ サイズが最初から決まっているなら、array の方が速い・小さい・安全です。vector はヒープを使うので少しオーバーヘッドあり。また、array は C++17 の constexpr 文脈で使える(コンパイル時に計算できる)利点があります。

Q. int a[5] (C の配列)ではダメ?
→ 動きますが、関数に渡すとサイズを別引数で渡す必要があり、境界チェックもない、= でコピーできない、など弱点が多い。std::array ならこれらすべて解決。

Q. サイズを変えたい場合は?
変えられません。サイズが変わる可能性があるなら最初から std::vector を使います。

2. C の生配列との違い

C の配列の何が不便かをまず確認します。

C の生配列C
int a[5] = {1,2,3,4,5}; // サイズを自分で計算 size_t n = sizeof(a) / sizeof(a[0]); // 関数に渡すとポインタに減衰 → サイズ不明に void f(int* arr) { /* サイズを別引数で */ } f(a); // a は int* に // = で代入できない int b[5]; // b = a; ← エラー
std::arrayC++
std::array<int, 5> a = {1,2,3,4,5}; // サイズはメソッドで auto n = a.size(); // 5 // 関数に渡しても型にサイズが含まれる void f(const std::array<int, 5>& a); f(a); // = でコピーできる std::array<int, 5> b = a;

ポインタ減衰(decay)とは?

C では関数に配列を渡すと、先頭要素へのポインタに変換されます。これを「ポインタ減衰」と呼びます。例:

減衰の例C
void f(int arr[5]) { // ← 書いても無視される // arr は実は int* (サイズ情報なし) size_t n = sizeof(arr) / sizeof(arr[0]); // n は 1 or 2 になる(ポインタのサイズ÷intのサイズ) } int a[5]; f(a);

これが C の関数では「サイズを別引数で渡す」ことが定番な理由です。std::array型の一部としてサイズを持つので減衰せず、関数内でも a.size() が正しく 5 を返します。

3. 基本操作(vector とほぼ同じ)

嬉しいことに、std::array の使い方はほぼ std::vector と同じです。新しく覚えることは「サイズを変える操作(push_back / erase / resize)が使えない」だけ。

① アクセス

vector と同じ API共通
std::array<int, 5> a = {10,20,30,40,50}; int x = a[1]; // 20 int y = a.at(1); // 20 (範囲チェックあり) int f = a.front(); // 10 int b = a.back(); // 50 int* p = a.data(); // &a[0] auto n = a.size(); // 5(常に 5)

② 範囲 for / イテレータ

vector と同じ共通
for (auto x : a) std::cout << x << ' '; for (auto& x : a) x *= 2; // 全要素 2 倍 // STL アルゴリズムも使える std::sort(a.begin(), a.end());

③ array 特有のメソッド

fill / swaparray 独自
a.fill(0); // 全要素を 0 で埋める std::array<int, 5> b; a.swap(b); // a と b の中身を高速交換

④ 使えない(= サイズが変わる)操作

NG な操作array では無い
// これらは全部 array にはない a.push_back(6); // ← コンパイルエラー a.pop_back(); // ← エラー a.erase(...); // ← エラー a.resize(10); // ← エラー a.clear(); // ← エラー

これらが必要なら最初から vector を選ぶ設計判断になります。

4. array vs vector vs 生配列 ― 比較表

項目int a[N] (C 配列)std::array<T, N>std::vector<T>
サイズ決定コンパイル時コンパイル時実行時(変更可)
メモリスタックスタックヒープ
size() メソッドなしありあり
関数渡しで情報保持減衰して喪失保持保持
= でコピー不可
at() 範囲チェックなしありあり
要素の追加・削除不可不可
constexpr で使えるC++20 で条件付き
速度・オーバーヘッド最速最速(配列と同等)わずかに遅い
使い分けの判断フロー:
  • サイズが変わるstd::vector
  • サイズ固定・コンパイル時に既知std::array
  • C 互換が必要/極小サイズの固定バッファ → C 配列(でも基本は array 推奨)
ここまでで std::array の基本は OK
残りは実用的な使い方。興味があれば。

5. よくある使い方

① 固定サイズの係数・テーブル

曜日名定数テーブル
const std::array<std::string, 7> weekdays = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; std::cout << weekdays[3]; // Wed

② 座標・ベクトル

3D 座標数値計算
using Vec3 = std::array<double, 3>; Vec3 p = {1.0, 2.0, 3.0}; Vec3 q = {4.0, 5.0, 6.0}; // 内積を計算 double dot = 0; for (size_t i = 0; i < 3; ++i) dot += p[i] * q[i];

③ 関数からの複数戻り値(pair/tuple の代替)

RGB を返す同じ型 3 つ
std::array<int, 3> get_rgb(int color) { return {(color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF}; } auto [r, g, b] = get_rgb(0x00FFA0);

同じ型の値を 3 つ返すなら tuple より array のほうがシンプル。構造化束縛で分解できるのも同じ。

C++17 の小ネタ: C++17 以降、std::array a{1, 2, 3}; と書くと型とサイズが自動推論されます(std::array<int, 3> と同じ)。template argument deduction (CTAD) の機能。
広告スペース

確認クイズ

std::array の理解度を 4 問で確認。

Q1. std::array<int, 5>push_back(6) を呼ぶとどうなる?

要素数が 6 になる
6 が末尾要素を置き換える
コンパイルエラー(array に push_back は無い)
実行時エラー
array はサイズ固定なので、要素数を変える push_back, pop_back, resize, erase, clear はすべて存在しません。コンパイルエラーで弾かれます(安全)。サイズを変えたいなら vector に切り替え。

Q2. C の int a[5]std::array<int, 5> の違いとして正しくないのは?

array は .size() メソッドを持つ
array は = で代入(コピー)できる
array は関数に渡してもサイズが保たれる
array はヒープに確保される
array はスタックに確保されます(vector はヒープ)。①②③はすべて array の利点として正しい。スタック確保なので高速で、ヒープを介さずに使えるのが array の強みです。

Q3. 次のうち、std::array を選ぶべきケースは?

ユーザー入力を読み込んで溜める(個数不明)
RGB(3 値)の色情報をまとめる
ログメッセージを時系列に蓄積する
CSV ファイルから読み取った行を保持する
array はサイズが最初から決まっていて変わらないケースに最適。RGB の 3 値、曜日の 7 つ、座標の x/y/z など。個数が実行時に決まる/変わるものは vector を使います。

Q4. std::array<int, 4> a{1,2,3,4}; a.fill(0); の後、a の中身は?

{1, 2, 3, 4}(変化なし)
{}(空)
{0, 0, 0, 0}
{0, 2, 3, 4}
fill(v) はすべての要素を v で埋めます。サイズは変わりません(常に 4)。array 特有の便利メソッド。「リセット」でよく使われます。