HOWTO · C++

C++ でベクトルを繰り返し処理する方法

C++ のベクトルを走査する4つの方法(インデックス付きループ、範囲ベースループ、イテレータ、`std::for_each`)を学びます。

このページの内容

std::vector のすべての要素を手軽に順番に見るなら、範囲ベースのforループを使います。要素の位置が必要ならインデックス付きループ、アルゴリズムにイテレータ範囲を渡すならイテレータ、各要素に呼び出し可能な処理を適用するなら std::for_each を選びます。以下の例は C++14 を対象としています。

インデックス付きの for ループでベクトルを走査する

インデックス付きループでは、要素とその位置の両方を扱えます。ループヘッダー内の3つの節はセミコロンで区切ります。インデックスを初期化し、size() より小さい間は処理を続け、その後インデックスを増やします。この完全な C++14 の例は書き方を示すもので、ここでは実行結果を主張していません。

#include <cstddef>
#include <iostream>
#include <vector>

int main() {
    const std::vector<int> values = {45, 54, 87, 78};

    for (std::size_t i = 0; i < values.size(); ++i) {
        std::cout << values[i] << ' ';
    }
    std::cout << '\n';
}

この例では values を const として宣言しているため、読み取り専用です。インデックスで指定した要素を調べるには values[i] を使えますが、値は変更できません。特定の位置を更新するには、ベクトルを変更可能な形で宣言し(たとえば std::vector<int> values = {45, 54, 87, 78};)、ループ内で values[i] に代入します。条件 i < values.size() によって、各インデックスがベクトル内の有効な要素位置に収まります。別のシーケンスの値と位置を対応させたり、選んだ要素を更新したりする場合は、インデックスによるアクセスが便利です。各値を順に読む、または変更するだけなら、インデックスを管理せずに済む範囲ベースのループのほうが通常は簡潔です。

size() は要素数を返し、インデックスは0から始まるため、要素がある場合の最後の要素の位置は size() - 1 です。条件に厳密な小なりを使うことで、要素数そのものをインデックスとして参照するのを防げます。ほかの要素型に合わせてループを変更するときは、インデックスの型をベクトルのサイズ型と互換性のあるものにしてください。

範囲ベースの for ループを使う

範囲ベースの for ループは、インデックスを管理せずに各要素を順に処理します。要素をコピーせずに読み取るには const auto& を使い、元の値を更新するには auto& を使います。この C++14 の例では両方の形式を示しています。ここでは実行結果を主張していません。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> values = {45, 54, 87, 78};

    for (const auto& value : values) {
        std::cout << value << ' ';
    }
    std::cout << '\n';

    for (auto& value : values) {
        value += 1;
    }
}

最初のループでは、各 value が読み取り専用の参照として結び付けられます。2つ目のループでは変更可能な参照を使うため、代入すると values 内の対応する要素が更新されます。値を調べるだけなら、意図が明確になる const 形式を使うのがおすすめです。

イテレータでベクトルを走査する

インターフェイスが位置の組を受け取る場合や、コンテナ内を明示的に進める必要がある場合は、イテレータを使います。begin() から始め、end() に達するまで処理します。end() イテレータが示すのは最後の要素の直後の位置です。現在のイテレータは間接参照できますが、end() 自体を間接参照してはいけません。この C++14 の例は走査方法を示すもので、ここでは実行結果を主張していません。

#include <iostream>
#include <vector>

int main() {
    const std::vector<int> values = {45, 54, 87, 78};

    for (std::vector<int>::const_iterator it = values.begin();
         it != values.end(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << '\n';
}

!= による比較は、一般的な半開イテレータ範囲 [begin(), end()) を表します。const_iterator を使うと、イテレータを通じた変更を防ぎながら各要素を読み取れます。

同じイテレータの組は標準アルゴリズムに渡せます。そのため、コードがすでにイテレータ範囲を扱っている場合は、明示的なイテレータ走査が便利です。読み取り専用のベクトルでは begin() と end() は定数イテレータを返します。変更可能なベクトルでは、返されるイテレータを間接参照して要素を更新できます。

std::for_each で処理を適用する

<algorithm> の std::for_each は、begin() から end() の直前までの半開範囲にある各要素へ、呼び出し可能な処理を適用します。ラムダを使うと、処理を走査のすぐそばに記述できます。この C++14 の例では、変更可能な参照を通じて各値に10を加えます。ここでは実行結果を主張していません。

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> values = {1, 2, 3};

    std::for_each(values.begin(), values.end(), [](int& value) {
        value += 10;
    });

    for (const auto& value : values) {
        std::cout << value << ' ';
    }
    std::cout << '\n';
}

呼び出し可能な処理で要素を読むだけなら、ラムダの引数には const int& を使います。単純に要素を順に見る場合は、範囲ベースのループのほうが通常は読みやすくなります。処理を呼び出し可能なオブジェクトとして渡す形が周囲のコードに合う場合は、std::for_each を選びます。4つの方法はいずれも同じベクトルの要素を走査しますが、位置や処理の扱い方が異なります。