Code Fix

中級

vector::at() の範囲外アクセスで terminate する原因

std::vectorの範囲外の要素に .at() でアクセスしたときに、例外std::out_of_rangeが投げられ、それを捕まえずにプログラムが終了してしまうバグです。

エラーメッセージの読み方

terminate called after throwing an instance of 'std::out_of_range'
terminate called after throwing an instance of 'std::out_of_range'
ランタイム(または例外機構、assert)が不整合を検知して出したメッセージ
Aborted
この後プロセスは強制終了します(Aborted)

このエラーが出る典型パターン

パターン1

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> nums = {1, 2, 3};
 5      std::cout << nums.at(10) << std::endl;
                              ^
 6      return 0;
 7  }
terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check: __n (which is 10) >= this->size() (which is 3) Aborted

.at()は範囲外アクセスを検知するとstd::out_of_range例外を投げます。それを捕まえるtry-catchが無いとプログラム全体がterminateで強制終了します。

直し方: 10 を 1 にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> scores = {70, 80, 90};
 5      std::cout << scores.at(5) << std::endl;
                               ^
 6      return 0;
 7  }
terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check: __n (which is 5) >= this->size() (which is 3) Aborted

要素数3のvectorに対して添字5でアクセスすると、5は0〜2の有効範囲を超えているためstd::out_of_rangeが投げられます。

直し方: 5 を 2 にします。

この問題を解いてみる →

パターン3

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> items = {5, 10};
 5      std::cout << items.at(7 ) << std::endl;
                               ^
 6      return 0;
 7  }
terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check: __n (which is 7) >= this->size() (which is 2) Aborted

operator[]と違い、.at()は範囲チェックのコストと引き換えに安全な例外を保証します。捕まえずに使うと未捕捉例外でプロセスが落ちます。

直し方: 7 を 0 にします。

この問題を解いてみる →

パターン4

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> data = {4, 8, 15, 16};
 5      std::cout << data.at(9 ) << std::endl;
                              ^
 6      return 0;
 7  }
terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check: __n (which is 9) >= this->size() (which is 4) Aborted

.at()は範囲外アクセスを検知するとstd::out_of_range例外を投げ、捕まえていないとプログラム全体が終了します。

直し方: 9 を 2 にします。

この問題を解いてみる →

パターン5

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> ids = {1, 2};
 5      std::cout << ids.at(6 ) << std::endl;
                             ^
 6      return 0;
 7  }
terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check: __n (which is 6) >= this->size() (which is 2) Aborted

要素数2のvectorに対して添字6は範囲外なので、std::out_of_rangeが投げられます。

直し方: 6 を 0 にします。

この問題を解いてみる →

よくある誤解

「vectorの添字アクセスはいつでも安全にチェックされる」というのは誤解です。角括弧の operator[] は範囲チェックをせず、範囲外でも黙って未定義動作になります。範囲チェック付きでアクセスしたいときに使うのが .at() で、その代わり例外が飛んできます。

実務での勘所

operator[]が範囲チェックをしないのは手抜きではなく、C++の「使わない機能にはコストを払わせない」というゼロオーバーヘッド原則に基づいた意図的な設計です。配列アクセスのたびに範囲チェックを行うと、大量のデータを扱うループで無視できない実行速度の低下につながります。安全性を優先したい場面ではコストを払ってでも.at()を使い、パフォーマンスが重要で範囲を外れないと確信できる場面ではoperator[]を使う、という使い分けが意図された設計です。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)