この記事のポイント

  • std::arrayによる固定長設計の利点・限界をレビューアー視点で整理
  • 生配列やvectorと比較した設計責務の違いを理解
  • API契約・イテレータ安全性・境界保証をレビュー対象化する力を養う

そもそもstd::arrayとは

std::arrayはC++11以降標準ライブラリで導入された固定長コンテナです。

  • 要素数はコンパイル時に決定
  • サイズ不変
  • 要素数はテンプレート引数で指定
std::array<int, 5> arr;
特性 説明
サイズ決定時期 コンパイル時
サイズ変更 不可
内部表現 内部に生配列を保持
境界チェック at()あり
イテレータ対応 STL互換
動的確保 行わない
  • 動的確保不要・RAII自動管理
  • 生配列とvectorの中間に位置付く安全設計要素

なぜこれをレビューするのか

レビューアー視点

std::array活用設計には以下のレビュー責任が伴う。

  • 固定長仕様の設計責任分離
    → なぜ動的長ではなく固定長で良いのか説明できるか

  • サイズ契約のAPI反映
    → 呼出側との契約で件数固定性が合意形成されているか

  • 境界安全性の実装保証
    → operator[]利用とat()利用の責任整理

  • コピー/ムーブ責任
    → 値型コピーを許容する設計意図整理

  • メモリ効率責任
    → 小サイズ頻出時の堅牢性・大サイズ利用時の責任整理

開発者視点

  • vectorと同列利用
  • operator[]で無警戒アクセス
  • 本質が「動的長不可」である点を理解しない
  • 構造体内メンバで安易に使い大型化誘発
  • コピーコスト無自覚

レビューアーはこれら「用途混同設計」を未然に防止する役割を担う。

良い実装例

ユースケース:APIレスポンスコード発生頻度集計用カウンタ

良い実装例:固定長カウンタ設計
#include <array>
#include <cstdint>

class ResponseCodeCounter {
public:
    void increment(int code) {
        if (code >= 200 && code <= 599) {
            counts_[code / 100 - 2]++;
        }
    }

    int getCount(int hundredClass) const {
        return counts_.at(hundredClass);
    }

private:
    std::array<int, 4> counts_{};  // 2xx, 3xx, 4xx, 5xx
};
  • 件数固定の要件が明確
  • 配列要素数が型定義時に固定化
  • at()利用により境界超過防止
  • 無動的確保で高信頼性

レビュー観点

  • 要素数固定理由が設計段階で明文化されているか
  • API契約が固定件数前提で整合しているか
  • at()利用により境界安全性が強化されているか
  • コピーコストを容認している用途か

良くない実装例: ケース1

以下はvector代替として誤用し、push_backを期待してしまった例。

vector代替誤用例
std::array<int, 10> arr;
arr.push_back(5);  // コンパイルエラー
@Reviewer
std::arrayは固定長です。可変長用途ならvectorを使用してください。

問題点

  • 可変長用途に流用
  • 構造選定設計責任の放棄

改善例

修正例:用途分離
// 可変長用途
std::vector<int> arr;
arr.push_back(5);
  • 可変長はvector
  • 固定長はarray

良くない実装例: ケース2

次はoperator[]利用で境界超過を誘発している例。

operator[]誤用例
std::array<int, 4> counts{};
int count = counts[5];  // UB
@Reviewer
境界超過防止のためat()使用を検討してください。

問題点

  • operator[]は境界チェックを行わない
  • 配列長保証契約がレビュー時に曖昧化

改善例

修正例:at()利用による境界安全化
int count = counts.at(2);
  • at()は境界違反時に例外throw
  • レビュー契約に明文化が望ましい

良くない実装例: ケース3

次は構造体内メンバで大型arrayを安易に持ち巨大オブジェクト化した例。

巨大固定配列内蔵例
struct LargeLogBuffer {
    std::array<char, 1024 * 1024> buffer;
};
@Reviewer
std::arrayは全要素を構造体内に持つため巨大化します。大型用途はvectorまたは動的設計を検討してください。

問題点

  • ヒープ設計すべき責務をスタック領域圧迫へ変換
  • ムーブ可能性喪失

改善例

修正例:大型領域はvector活用
struct LargeLogBuffer {
    std::vector<char> buffer;
};
  • 巨大arrayは移動不能化の危険性大

API契約と固定長保証の整理パターン

パターンA:完全固定仕様

struct RequestIdSet {
    std::array<std::string, 10> ids;
};
  • 件数がビジネス仕様として完全固定
  • API契約も暗黙的に固定長

パターンB:柔軟長API分離

std::vector<std::string> fetchRequestIds();
  • 件数可変性が許容される契約
  • 固定長使用は内部変換側で責任保持

std::array・vector・生配列比較整理

項目 生配列 std::array vector
サイズ変更 不可 不可
サイズ取得 不可(sizeof) size() size()
境界チェック なし at()あり at()あり
STL互換性 低い 高い 高い
ムーブ可否 難しい
内部所有権 自動 自動 自動
  • 生配列は事実上排除対象
  • std::arrayは「固定長保証」という契約目的

PlantUMLで責任整理

UML Diagram

観点チェックリスト

実務レビューFAQ

Q1. 固定長なら生配列でも良くない?
→ ほぼすべての現代設計で生配列は非推奨。安全性・ライフサイクル管理でstd::array優位。

Q2. 大きなstd::arrayを構造体に入れていい?
→ 避けるべき。大型領域は動的設計優先。

Q3. operator[]は絶対禁止?
→ 読み取り専用用途で範囲保証が100%成立するなら容認可能。境界超過可能性が残るならat()利用推奨。

Q4. std::arrayのムーブは安全?
→ ムーブ可能。ただし大型時はコピー負荷に注意。

Q5. vectorからarrayに移行する条件?
→ 「件数が不変であることが設計上保証される」時のみ。

まとめ

std::arrayは固定長という契約責任を設計段階に埋め込むための型です。
レビューアーは、

  • 件数保証責任
  • 動的確保排除責任
  • 境界超過防止責任
  • API契約への反映責任

これらをコード表面ではなく設計全体から読み解くことが求められます。
固定長契約レビューはAPI契約責任整理の最良教材の一つです。