[C++]  [i] と at(i) の違い



## In a nutshell

C++における配列的な要素アクセスには，`[i]`(添字演算子)と `at(i)`(メンバ関数)の2つがある．どちらも似たように使えるが，安全性・エラーハンドリング・パフォーマンスの面で大きく異なる．両方の選択肢が利用できるのであれば， `at(i)` を利用する方が安全である．



## はじめに

C++ には，`std::vector` や `std::string` などのコンテナから要素を取得する方法として `[i]` と `at(i)` がある．普段は特に意識せず `[i]` を使用していたが，`at(i)` を使っているスクリプトを見た際に，この違いについて理解していない事に気が付いた．そこで，両者の違いについて，以下に纏めた．



## 予想される読み手
 - C++に関して初学者である



## 基本的な仕様の違い(表と解説)
`vec[i]` は配列のように要素へ直接アクセスでき，パフォーマンスも高いが，インデックスが有効かどうかのチェックが行われないため，無効なインデックスを指定した場合，未定義動作(クラッシュや意図しない動作)となる．一方で `vec.at(i)` は，アクセス前にインデックスの範囲チェックを自動で行い，無効な場合は `std::out_of_range` 例外を投げる．そのため，安全に利用できるが，若干パフォーマンスに劣る．


| 機能        | `[i]`                   | `at(i)`                          |
|-------------|--------------------------|----------------------------------|
| 範囲チェック | なし(未定義動作)       | あり(範囲外で例外を投げる)     |
| 戻り値型     | 要素の参照(読み書き可能)| 要素の参照(読み書き可能)       |
| 例外発生     | なし                     | 範囲外で `std::out_of_range`     |
| パフォーマンス | 高い                    | やや低い(範囲チェックあり)     |
| noexcept     | yes                     | no(例外を投げる可能性あり)     |



## 具体的な違いとコード例
### `[i]` の使用例

範囲外にアクセスしても，エラーを吐かずにバグを引き起こすことがある．したがって，検知されにくく，大変に厄介で危険である．


```cpp
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {10, 20, 30};
    std::cout << vec[1] << std::endl; // 出力: 20
    std::cout << vec[3] << std::endl; // 範囲外 -> 未定義動作
}
```



### `at(i)` の使用例

`at()` を使うことで，安全にエラー検知が可能．バグ等の回避に貢献する．


```cpp
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {10, 20, 30};
    try {
        std::cout << vec.at(1) << std::endl; // 出力: 20
        std::cout << vec.at(3) << std::endl; // 例外が発生
    } catch (std::out_of_range& e) {
        std::cerr << "範囲外アクセス: " << e.what() << std::endl;
    }
}
```




## 使い分け

### `at(i)` を使うべき場面
- 安全性を重視する
- デバッグ中
- ユーザー入力など，外部からのインデックス指定がある
- 本番環境で不正アクセスを，明示的に防ぎたい

### `[i]` を使うべき場面
- パフォーマンスが極めて重要なケース
- アクセスするインデックスが常に正しいと保証されている
- 大量の要素に対してループ処理をする(速度優先)



## 注意点・例外的なこと

### その他のコンテナとの関係

- `std::array`, `std::string` も `at()` を持つ．
- `std::map`, `std::unordered_map` の `operator[]` は，存在しないキーにアクセスすると新しい要素が追加される．
- `at()` は同様の状況で例外を投げるため，意図しない挿入を防げる．

```cpp
#include <map>
#include <iostream>

int main() {
    std::map<std::string, int> mp;
    mp["key1"] = 10;
    std::cout << mp["key2"] << std::endl; // 0が挿入される

    try {
        std::cout << mp.at("key3") << std::endl; // 例外発生
    } catch (std::out_of_range& e) {
        std::cout << "存在しないキー: " << e.what() << std::endl;
    }
}
```



## 特殊ケース・補足事項

### `const` に対する動作

読み取り専用でも `[i]`, `at(i)` の両方は使えるが，書き込みはできない．

```cpp
const std::vector<int> v = {1, 2, 3};
int x = v[1];      // 読み取りのみ可能
int y = v.at(2);   // 読み取りのみ可能
```



### `std::vector<bool>` の特殊性

`std::vector<bool>` は特殊化されており，`[i]` や `at(i)` の戻り値は `bool&` ではなくプロキシオブジェクトになる．このため，ポインタ取得や参照渡しなど一部の操作が制限される．

```cpp
std::vector<bool> flags = {true, false};
auto val = flags[0];  // 実際には bool ではない
```


### 多次元配列へのアクセス

ネストされたコンテナでは，`at()` を二重に使うことで，階層ごとに安全なアクセスが可能．

```cpp
std::vector<std::vector<int>> matrix = {{1, 2}, {3, 4}};
int a = matrix[1][0];             // 危険(未定義動作の可能性)
int b = matrix.at(1).at(0);       // 安全なアクセス
```



### 例外処理の有無による関数設計の違い

- `at()` は `noexcept` ではないため，例外を許容しない関数では使用に注意．
- `noexcept` 指定された関数内では使用できないケースもある．



### C++20以降の `std::span` の活用

- `std::span` は軽量なビュー型で，安全性と柔軟性を兼ね備える．
- インデックスアクセスに `at()` のような安全機構を追加できる設計も可能．



## まとめ

| 観点          | `[i]`                         | `at(i)`                            |
|---------------|--------------------------------|------------------------------------|
| 安全性         | 低い(未定義動作のリスク)     | 高い(例外で保護される)           |
| パフォーマンス | 高い(範囲チェックなし)       | やや低い(範囲チェックあり)       |
| デバッグ適性   | 低い(バグに気づきにくい)     | 高い(例外で検出しやすい)         |
| 推奨場面       | 高速処理・内部処理             | ユーザー入力・教育・バグ予防       |
| noexcept       | yes                            | no(例外を投げる可能性がある)     |
| 対応コンテナ   | ほとんどのコンテナ              | 一部コンテナ(例:list にはなし)  |



## Summary

- `at(i)` は 安全性が高く例外を投げる．
- `[i]` は 高速だが，範囲外アクセスは未定義動作．
- 初心者や安全重視の開発では `at()` を使うのが推奨される．
- パフォーマンスを優先する場面では `[i]` も選択肢になるが，慎重な設計が求められる．



## References

[cppreference.com -  C++ > Containers library > std::vector](https://en.cppreference.com/w/cpp/container/vector)
[cppreference.com -  C++ > Containers library >std::map](https://en.cppreference.com/w/cpp/container/map)