名前空間
変種
操作

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

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
for
range-for (C++11)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (C++20未満)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
ラベル
label : statement
式文
expression ;
複文
{ statement... }
選択文
if
switch
繰り返し文
while
do-while
for
範囲 for(C++11)
ジャンプ文
break
continue
return
goto
宣言文
declaration ;
try ブロック
try compound-statement handler-sequence
トランザクショナルメモリ
synchronized, atomic_commit など(TM TS)
 

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

コンテナ内のすべての要素などの値の範囲に対して行う、伝統的な for ループのより可読性の高い同等品として使用されます。

目次

[編集] 構文

attr(オプション) for ( range_declaration : range_expression ) loop_statement (C++20未満)
attr(オプション) for ( init-statement(オプション)range_declaration : range_expression )

loop_statement

(C++20以上)
attr - 任意の個数の属性
init-statement(C++20) - 以下のいずれか。
  • 式文 (空文 ";" でも構いません)。
  • 単純宣言。 一般的には初期化子付きの変数の宣言ですが、好きなだけ多くの変数を宣言しても構いませんし、構造化束縛の宣言であっても構いません。
あらゆる init-statement はセミコロン ; で終わらなければならないことに注意してください。 これが式または宣言にセミコロンが続いたものとしてしばしば非形式的に説明される理由です。
range_declaration - range_expression によって表されるシーケンスの要素の型、またはその型への参照の、名前付き変数の宣言。 しばしば自動的な型推定のための auto 指定子が使用されます。
range_expression - 適切なシーケンス (配列、または begin および end メンバ関数または自由関数が定義されているオブジェクト (後述)) または波括弧初期化子リストを表す任意の
loop_statement - 任意の。 一般的には複文であり、ループの本体となります。

range_declaration構造化束縛の宣言でも構いません。

for (auto&& [first,second] : mymap) {
    // first と second を使う
}
(C++17以上)

[編集] 説明

上記の構文は以下と同等なコードになります (__range__begin および __end は説明専用です)。

{

auto && __range = range_expression ;
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}

}

(C++17未満)

{

auto && __range = range_expression ;
auto __begin = begin_expr ;
auto __end = end_expr ;
for ( ; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}

}

(C++17以上)
(C++20未満)

{

init-statement
auto && __range = range_expression ;
auto __begin = begin_expr ;
auto __end = end_expr ;
for ( ; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}

}

(C++20以上)

range_expression がイテレートするシーケンスまたは範囲を決定するために評価されます。 シーケンス内の各要素が順番に、逆参照され、 range_declaration で指定された型と名前を持つ変数に代入されます。

begin_expr および end_expr は以下のように定義されます。

  • range_expression が配列型の式の場合、 begin_expr__range で、 end_expr(__range + __bound) です。 ただし __bound はその配列の要素数です (配列のサイズが不明であるか、不完全型の配列の場合、プログラムは ill-formed です)。
  • range_expressionbegin という名前のメンバと end という名前のメンバの両方を持つクラス型 C の式の場合 (それらのメンバの型やアクセス可能性に関わらず)、 begin_expr__range.begin() で、 end_expr__range.end() です。
  • そうでなければ、 begin_exprbegin(__range) で、 end_exprend(__range) です。 ただし、これらは実引数依存の名前探索によってのみ探されます (非 ADL の名前探索は行われません)。

伝統的なループと同様に、ループを早期に終了するために break 文を使用することができ、次の要素からループを再開するために continue 文を使用することができます。

[編集] 一時オブジェクトの範囲式

range_expression が一時オブジェクトを返した場合、その生存期間は、転送参照 __range への束縛によって表されている通り、ループの終わりまで延長されますが、 range_expression 内の一時オブジェクトの生存期間が延長されないことには注意が必要です。

for (auto& x : foo().items()) { /* .. */ } // foo() が値で返す場合は未定義動作。

この問題は init-statement を用いてワークアラウンドできます。

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

[編集] ノート

初期化子 (range_expression) が波括弧初期化子リストの場合は、 __range は std::initializer_list<>&& であると推定されます。

転送参照への推定 for (auto&& var : sequence) を用いることは、安全であり、実際、総称コードでは推奨されます。

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

range_declaration で宣言された変数は、通常、 loop_statement で使用されますが、そうしなければならないわけではありません。

C++17 以降、 begin_exprend_expr の型が同じである必要はなく、実際、 end_expr の型がイテレータである必要はありません。 等しくないかどうかを比較できる必要があるだけです。 これは範囲を述語 (例えば「イテレータがヌル文字を指している」など) によって区切ることを可能とします。

コピーオンライトの意味論を持つ (非 const な) オブジェクトに対して使用するとき、範囲ベースの for ループは非 const な begin() メンバ関数を (暗黙に) 呼ぶことによってディープコピーを発生させる場合があります。 これが望ましくない場合 (例えばループが実際にはオブジェクトを変更しないなど) は、 std::as_const を使用することができます。

struct cow_string { /* ... */ }; // コピーオンライト文字列
cow_string str = /* ... */;
 
// for(auto x : str) { /* ... */ } // ディープコピーが発生するかもしれません。
 
for(auto x : std::as_const(str)) { /* ... */ }

[編集] キーワード

for

[編集] 欠陥報告

以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。

DR 適用先 発行時の動作 正しい動作
P0962R1 C++11 member interpretation is used if either member begin and end is present only used if both are present

[編集]

#include <iostream>
#include <vector>
 
int main() {
    std::vector<int> v = {0, 1, 2, 3, 4, 5};
 
    for (const int& i : v) // const 参照でアクセスします。
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (auto i : v) // 値でアクセスします。 i の型は int です。
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (auto&& i : v) // 転送参照でアクセスします。 i の型は int& です。
        std::cout << i << ' ';
    std::cout << '\n';
 
    const auto& cv = v;
 
    for (auto&& i : cv) // 転送参照でアクセスします。 i の型は const int& です。
        std::cout << i << ' ';
    std::cout << '\n';
 
    for (int n : {0, 1, 2, 3, 4, 5}) // 初期化子は波括弧初期化子リストでも構いません。
        std::cout << n << ' ';
    std::cout << '\n';
 
    int a[] = {0, 1, 2, 3, 4, 5};
    for (int n : a) // 初期化子は配列でも構いません。
        std::cout << n << ' ';
    std::cout << '\n';
 
    for ([[maybe_unused]] int n : a)  
        std::cout << 1 << ' '; // ループ変数を使用する必要はありません。
    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
1 1 1 1 1 1

[編集] 関連項目

指定範囲の要素に関数を適用します
(関数テンプレート) [edit]