【C++】範囲for文(for auto)とは?|vectorやmapの要素を簡潔に回す書き方

今回は、C++でかなり便利な「範囲for文(for auto)」について解説していきます。

「for文は書けるけど、範囲for文って何?」
「vectorを回す時に i < size() を毎回書かないといけないの?」
「mapを簡潔に回す方法がよく分からない…」

こんな疑問はありませんか?

範囲for文は、配列・vector・mapなどの要素を先頭から順番にシンプルに回せる書き方です。添字を書かなくていいので、特に初心者のうちはかなり見やすくなります。

この記事を読み終えると、あなたは範囲for文の意味・基本の書き方・vectorやmapでの使い方・autoauto& の違い・初心者がハマりやすいミスまでしっかり理解できるようになると思いますので、ぜひ最後まで読んでいただけると嬉しいです。

範囲for文(for auto)とは?

範囲for文とは、配列やコンテナの要素を1つずつ取り出して処理するためのfor文です。

基本の形は次のようになります。

for (auto 要素 : コンテナ) {
    処理
}

たとえばvectorの中身を順番に表示したい時、従来のfor文よりかなり短く書けます。ループ全体の考え方がまだ不安な方は、for文とwhile文の違いの記事もあわせて読むと理解しやすいです。

なぜ範囲for文が便利なのか?

普通のfor文でも要素を回せますが、毎回 i = 0i < size()++i と書く必要があります。

一方で範囲for文なら、「このコンテナの全要素を順番に処理したい」という意図をそのまま書けます。つまり、回数ではなく、要素そのものに注目して書けるのが大きな強みです。

特に「全部表示したい」「全部更新したい」といった処理ではかなり読みやすくなります。

vectorを範囲for文で回す基本

まずは一番分かりやすいvectorの例を見てみましょう。

▼main.cpp


#include <iostream>
#include <vector>

int main() {
    std::vector<int> scores = { 10, 20, 30 };

    for (auto score : scores) {
        std::cout << score << "\n";
    }

    return 0;
}

実行結果

10
20
30

このように、添字を使わなくても全要素を順番に取り出せます。単純に全件表示するだけなら、かなり書きやすいです。

mapを範囲for文で回す基本

mapでも範囲for文はかなり便利です。ただし、mapの1要素は「キーと値のセット」なので、vectorの時より少しだけ意識する点があります。

▼main.cpp


#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> playerHp;
    playerHp["Hero"] = 100;
    playerHp["Mage"] = 80;

    for (auto item : playerHp) {
        std::cout << item.first << " : " << item.second << "\n";
    }

    return 0;
}

実行結果

Hero : 100
Mage : 80

mapでは、item.first がキー、item.second が値です。最初は少し見慣れないですが、慣れるとかなり便利です。

auto と auto& の違いは?

ここは初心者がかなりハマりやすいポイントです。

  • auto:要素をコピーして受け取る
  • auto&:元の要素を参照で受け取る

つまり、ループの中で元のデータを書き換えたいなら、auto ではなく auto& が必要になります。

▼main.cpp


#include <iostream>
#include <vector>

int main() {
    std::vector<int> scores = { 10, 20, 30 };

    for (auto& score : scores) {
        score += 5;
    }

    for (auto score : scores) {
        std::cout << score << "\n";
    }

    return 0;
}

実行結果

15
25
35

このように、auto& にすると元のvectorの中身そのものを変更できます。参照の考え方がまだ曖昧な方は、今後の関連記事として参照記事と合わせて読むとかなり整理しやすいと思います。

【重要】私が実際に範囲for文で困った体験談

私も自主制作ゲームで、敵のHPリストをvectorで管理していた時に、範囲for文を使って一括でダメージを与えようとしたことがあります。

その時、for (auto hp : enemyHp) と書いていたのですが、画面上では何も変わりませんでした。最初は「ループ自体が回っていないのかな?」と思ったのですが、実際にはコピーした hp を変更していただけで、元のvectorは変わっていなかったんですね。

そこで auto& に直したところ、ちゃんと元データが更新されるようになりました。範囲for文は便利ですが、コピーなのか参照なのかを意識しないと、かなりハマりやすいと実感しました。

範囲for文使用時のよくある失敗例と対処法

1. auto で受けているのに、元データが変わると思ってしまう

  • 失敗例:for (auto x : values) の中で x を変更する
  • 対処法:元データを更新したいなら auto& を使う

2. mapの要素を普通の値だと思ってしまう

  • 失敗例:item を int や string のように扱ってしまう
  • 対処法:mapはキーと値のセットなので、item.firstitem.second を使う

3. 添字が必要な処理でも無理に範囲for文を使おうとする

  • 失敗例:インデックス番号が必要なのに範囲for文だけで書こうとする
  • 対処法:添字が必要な時は普通のfor文を使う

実践例:Playerクラスのvectorを範囲for文で回す

ゲーム制作では、クラスの配列やvectorをまとめて回したい場面がかなり多いです。そういう時に範囲for文はかなり相性がいいです。クラスの考え方がまだ不安な方は、クラスの記事も参考になります。

▼main.cpp


#include <iostream>
#include <string>
#include <vector>

class Player {
public:
    std::string name;
    int hp;
};

int main() {
    std::vector<Player> players = {
        { "Hero", 100 },
        { "Mage", 80 },
        { "Knight", 120 }
    };

    for (auto& player : players) {
        player.hp -= 10;
    }

    for (const auto& player : players) {
        std::cout << player.name << " : " << player.hp << "\n";
    }

    return 0;
}

実行結果

Hero : 90
Mage : 70
Knight : 110

このように、更新時は auto&、表示だけなら const auto& という使い分けがかなり便利です。

注意点

  • 全要素を順番に処理するなら範囲for文はかなり読みやすい
  • 元データを変更したい時は auto& を使う
  • 読み取りだけなら const auto& も便利
  • 添字が必要な処理では普通のfor文の方が向いている

もし「このループで何回回っているのか分からない」「参照で受けているつもりなのに値が変わらない」といった時は、Visual Studioでのデバッグ方法もかなり役立ちます。ブレークポイントで止めて確認すると、コピーなのか参照なのかも見えやすいです。

まとめ

  • 範囲for文は、配列やvector、mapなどの要素を簡潔に回す書き方
  • for (auto x : container) が基本形
  • 元データを変更したいなら auto& を使う
  • mapでは item.first がキー、item.second が値
  • 添字が不要な全件処理では、かなり読みやすく書ける

個人的には、「全要素を順番に見るだけなら、まず範囲for文を考える」くらいでちょうどいいと思います。書き方が短くなるだけでなく、コードの意図もかなり伝わりやすくなります。

関連記事