名前空間
変種
操作

範囲ベースのforループ (C++11以降)

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲-for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

範囲に対してforループを実行する。

コンテナ内のすべての要素など、値の範囲に対して操作する従来のforループの、より読みやすい等価物として使用される。

目次

[編集] 構文

attr (optional) for ( init-statement (optional) item-declaration : range-initializer ) statement
attr - 任意の数の属性
init-statement - (C++20以降) 次のいずれか
(C++23から)

どのinit-statementもセミコロンで終わらなければならないことに注意してください。これが、しばしば非公式に式または宣言の後にセミコロンが続くと説明される理由です。

item-declaration - 各範囲項目に対する宣言
range-initializer - またはブレース囲み初期化子リスト
statement - 任意のステートメント (通常は複合ステートメント)

[編集] 説明

上記の構文は、以下のコードと同等のコードを生成する。ただし、range-initializerの一時オブジェクトの寿命拡張は除く (下記を参照)(C++23以降) (/* */で囲まれた変数と式は説明のためのみである)

{

auto&& /* range */ = range-initializer ;
for (auto /* begin */ = /* begin-expr */, /* end */ = /* end-expr */;
/* begin */ != /* end */; ++/* begin */)
{
item-declaration = */* begin */;
statement
}

}

(C++17まで)

{

auto&& /* range */ = range-initializer ;
auto /* begin */ = /* begin-expr */;
auto /* end */ = /* end-expr */;
for ( ; /* begin */ != /* end */; ++/* begin */)
{
item-declaration = */* begin */;
statement
}

}

(C++17以降)
(C++20まで)

{

init-statement
auto&& /* range */ = range-initializer ;
auto /* begin */ = /* begin-expr */;
auto /* end */ = /* end-expr */;
for ( ; /* begin */ != /* end */; ++/* begin */)
{
item-declaration = */* begin */;
statement
}

}

(C++20以降)

range-initializer は、反復するシーケンスまたは範囲を初期化するために評価される。シーケンスの各要素は順に逆参照され、item-declarationで与えられた型と名前を持つ変数を初期化するために使用される。

item-declaration は次のいずれかである。

説明専用の式/* begin-expr *//* end-expr */は次のように定義される。

  • /* range */の型が配列型Rへの参照である場合
  • Rが境界Nの場合、/* begin-expr *//* range */であり、/* end-expr *//* range */ + Nである。
  • Rが未知の境界の配列または不完全な型の配列である場合、プログラムは不適格である。
  • /* range */の型がクラス型Cへの参照であり、Cのスコープ内で「begin」と「end」という名前を検索するとそれぞれ少なくとも1つの宣言が見つかる場合、/* begin-expr *//* range */.begin()であり、/* end-expr *//* range */.end()である。
  • それ以外の場合、/* begin-expr */begin(/* range */)であり、/* end-expr */end(/* range */)であり、「begin」と「end」は引数依存の名前探索 (ADL以外の名前探索は行われない) を介して見つけられる。

statement の中でループを終了する必要がある場合、終了文として breakを使用できます。

statement の中で現在のイテレーションを終了する必要がある場合、ショートカットとして continueを使用できます。

init-statementで導入された名前がstatementの最も外側のブロックで再宣言された場合、プログラムは不適格である。

for (int i : {1, 2, 3})
    int i = 1; // error: redeclaration

[編集] 一時的な範囲初期化子

range-initializerが一時オブジェクトを返す場合、その寿命は転送参照/* range */へのバインドによって示されるように、ループの終わりまで延長される。

range-initializer内のすべての一時オブジェクトの寿命は延長されない。ただし、range-initializerの終わりで破棄される場合は除く(C++23以降)

// if foo() returns by value
for (auto& x : foo().items()) { /* ... */ } // until C++23 undefined behavior

この問題はinit-statementを使用して回避できる。

for (T thing = foo(); auto& x : thing.items()) { /* ... */ } // OK
(C++20以降)


C++23でも中間関数呼び出しの非参照パラメータは寿命延長されない (なぜなら、一部のABIでは呼び出し元ではなく呼び出し先で破棄されるため)。しかし、これはそもそもバグのある関数にとってのみ問題である。

using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t)        { return t; } // always returns a dangling reference
T g();
 
void foo()
{
    for (auto e : f1(g())) {} // OK: lifetime of return value of g() extended
    for (auto e : f2(g())) {} // UB: lifetime of f2's value parameter ends early
}
(C++23から)

[編集] 注釈

range-initializerブレース囲み初期化子リストである場合、/* range */std::initializer_listへの参照であると推論される。

ジェネリックコードでは、転送参照への推論for (auto&& var : sequence)を使用することが安全であり、実際には推奨される。

範囲の型に「begin」という名前のメンバと「end」という名前のメンバがある場合、メンバ解釈が使用される。これは、メンバが型、データメンバ、関数、列挙子のいずれであるか、またそのアクセス可能性に関係なく行われる。したがって、class meow { enum { begin = 1, end = 2 }; /* rest of class */ };のようなクラスは、名前空間スコープの「begin」/「end」関数が存在しても、範囲ベースのforループでは使用できない。

item-declarationで宣言された変数は通常statementで使用されるが、その必要はない。

C++17以降、/* begin-expr *//* end-expr */の型は同じである必要はなく、実際、/* end-expr */の型はイテレータである必要もない。単に、一方と比較して不等比較できる必要がある。これにより、述語によって範囲を区切ることができる (例:「イテレータがヌル文字を指している」)。

(C++17以降)

コピーオンライトセマンティクスを持つ (非const) オブジェクトで使用する場合、範囲ベースのforループは、(暗黙的に) 非constのbegin()メンバ関数を呼び出すことでディープコピーをトリガーする可能性がある。

それが望ましくない場合 (例えば、ループが実際にはオブジェクトを変更しないため)、std::as_constを使用することで回避できる。

struct cow_string { /* ... */ }; // a copy-on-write string
cow_string str = /* ... */;
 
// for (auto x : str) { /* ... */ } // may cause deep copy
 
for (auto x : std::as_const(str)) { /* ... */ }
(C++17以降)
機能テストマクロ 規格 機能
__cpp_range_based_for 200907L (C++11) 範囲ベースのforループ
201603L (C++17) 異なるbegin/end型を持つ範囲ベースのforループ
202211L (C++23) range-initializer内のすべての一時オブジェクトの寿命延長

[編集] キーワード

for

[編集]

#include <iostream>
#include <vector>
 
int main()
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5};
 
    for (const int& i : v) // access by const reference
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (auto i : v) // access by value, the type of i is int
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (auto&& i : v) // access by forwarding reference, the type of i is int&
        std::cout << i << ' ';
    std::cout << '\n';
 
    const auto& cv = v;
 
    for (auto&& i : cv) // access by f-d reference, the type of i is const int&
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (int n : {0, 1, 2, 3, 4, 5}) // the initializer may be a
                                     // braced-enclosed initializer list
        std::cout << n << ' ';
    std::cout << '\n';
 
    int a[] = {0, 1, 2, 3, 4, 5};
    for (int n : a) // the initializer may be an array
        std::cout << n << ' ';
    std::cout << '\n';
 
    for ([[maybe_unused]] int n : a)  
        std::cout << 1 << ' '; // the loop variable need not be used
    std::cout << '\n';
 
    for (auto n = v.size(); auto i : v) // the init-statement (C++20)
        std::cout << --n + i << ' ';
    std::cout << '\n';
 
    for (typedef decltype(v)::value_type elem_t; elem_t i : v)
    // typedef declaration as init-statement (C++20)
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (using elem_t = decltype(v)::value_type; elem_t i : v)
    // alias declaration as init-statement (C++23)
        std::cout << i << ' ';
    std::cout << '\n';
}

出力

0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
1 1 1 1 1 1 
5 5 5 5 5 5 
0 1 2 3 4 5 
0 1 2 3 4 5

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 1442 C++11 非メンバの検索が未指定であった
begin」と「end」は通常の非修飾検索を含む
通常の非修飾検索は行われない
CWG 2220 C++11 init-statementで導入された名前が再宣言される可能性がある この場合、プログラムは不適格である
CWG 2825 C++11 range-initializerがブレース囲み初期化子リストである場合、
非メンバの「begin」と「end」が検索される
この場合、メンバの「begin
と「end」が検索される
P0962R1 C++11 メンバ解釈は、どちらか一方がある場合に使用された
メンバの「begin」と「end」が存在する
両方が存在する場合のみ使用される

[編集] 関連項目

範囲内の要素に単項関数オブジェクトを適用する
(関数テンプレート) [編集]
English 日本語 中文(简体) 中文(繁體)