C++レビュー|std::arrayの固定長利用保証と設計責任整理
この記事のポイント
- 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を期待してしまった例。
std::array<int, 10> arr;
arr.push_back(5); // コンパイルエラー
@Reviewerstd::arrayは固定長です。可変長用途ならvectorを使用してください。
問題点
- 可変長用途に流用
- 構造選定設計責任の放棄
改善例
// 可変長用途
std::vector<int> arr;
arr.push_back(5);- 可変長はvector
- 固定長はarray
良くない実装例: ケース2
次はoperator[]利用で境界超過を誘発している例。
std::array<int, 4> counts{};
int count = counts[5]; // UB
@Reviewer境界超過防止のためat()使用を検討してください。
問題点
- operator[]は境界チェックを行わない
- 配列長保証契約がレビュー時に曖昧化
改善例
int count = counts.at(2);- at()は境界違反時に例外throw
- レビュー契約に明文化が望ましい
良くない実装例: ケース3
次は構造体内メンバで大型arrayを安易に持ち巨大オブジェクト化した例。
struct LargeLogBuffer {
std::array<char, 1024 * 1024> buffer;
};
@Reviewerstd::arrayは全要素を構造体内に持つため巨大化します。大型用途は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で責任整理
観点チェックリスト
実務レビューFAQ
Q1. 固定長なら生配列でも良くない?
→ ほぼすべての現代設計で生配列は非推奨。安全性・ライフサイクル管理でstd::array優位。
Q2. 大きなstd::arrayを構造体に入れていい?
→ 避けるべき。大型領域は動的設計優先。
Q3. operator[]は絶対禁止?
→ 読み取り専用用途で範囲保証が100%成立するなら容認可能。境界超過可能性が残るならat()利用推奨。
Q4. std::arrayのムーブは安全?
→ ムーブ可能。ただし大型時はコピー負荷に注意。
Q5. vectorからarrayに移行する条件?
→ 「件数が不変であることが設計上保証される」時のみ。
まとめ
std::arrayは固定長という契約責任を設計段階に埋め込むための型です。
レビューアーは、
- 件数保証責任
- 動的確保排除責任
- 境界超過防止責任
- API契約への反映責任
これらをコード表面ではなく設計全体から読み解くことが求められます。
固定長契約レビューはAPI契約責任整理の最良教材の一つです。
