Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/range-for"

From cppreference.com
< cpp‎ | language
(Added note that the explanation code is not equivalent)
m (Minor fix.)
 
(17 intermediate revisions by 11 users not shown)
Line 1: Line 1:
{{title|Range-based for loop {{mark since c++11}}}}
+
{{title|Range-based {{tt|for}} loop {{mark since c++11}}}}
 
{{cpp/language/statements/navbar}}
 
{{cpp/language/statements/navbar}}
Executes a for loop over a range.
+
Executes a {{c/core|for}} loop over a range.
  
Used as a more readable equivalent to the traditional {{rlp|for|for loop}} operating over a range of values, such as all elements in a container.
+
Used as a more readable equivalent to the traditional {{rlp|for|{{c/core|for}} loop}} operating over a range of values, such as all elements in a container.
  
 
===Syntax===
 
===Syntax===
 
{{sdsc begin}}
 
{{sdsc begin}}
{{sdsc|{{spar|attr}}{{mark optional}} {{ttb|for (}} {{spar|init-statement}}{{mark optional}} {{spar|range-declaration}} {{ttb|:}} {{spar|range-expression}} {{ttb|)}}
+
{{sdsc|{{spar optional|attr}} {{ttb|for (}} {{spar optional|init-statement}} {{spar|item-declaration}} {{ttb|:}} {{spar|range-initializer}} {{ttb|)}} {{spar|statement}}}}
{{spar|loop-statement}}}}
+
 
{{sdsc end}}
 
{{sdsc end}}
  
 
{{par begin}}
 
{{par begin}}
 
{{par|{{spar|attr}}|any number of {{rlp|attributes}}}}
 
{{par|{{spar|attr}}|any number of {{rlp|attributes}}}}
{{par|{{spar|init-statement}}|{{mark since c++20}} either
+
{{par|{{spar|init-statement}}|{{mark since c++20}} one of
* an {{rlp|statements|expression statement}} (which may be a ''null statement'' "{{ttb|;}}")
+
* an {{rlp|statements|expression statement}} (which may be a null statement {{c|;}})
* a {{rlp|declarations|simple declaration}}, typically a declaration of a variable with initializer, but it may declare arbitrarily many variables or be a {{rlp|structured binding|structured binding declaration}}
+
* a {{rlp|declarations|simple declaration}} (typically a declaration of a variable with initializer), it may declare arbitrarily many variables or be a {{rlp|structured binding|structured binding declaration}}
 
{{rrev|since=c++23|
 
{{rrev|since=c++23|
 
* an {{rlp|type alias|alias declaration}}
 
* an {{rlp|type alias|alias declaration}}
 
}}
 
}}
:Note that any {{spar|init-statement}} must end with a semicolon {{ttb|;}}, which is why it is often described informally as an expression or a declaration followed by a semicolon.}}
+
Note that any {{spar|init-statement}} must end with a semicolon. This is why it is often described informally as an expression or a declaration followed by a semicolon.}}
{{par|{{spar|range-declaration}}|a {{rlp|declarations|declaration}} of a named variable, whose type is the type of the element of the sequence represented by {{spar|range-expression}}, or a reference to that type. Often uses the {{rlp|auto|auto specifier}} for automatic type deduction}}
+
{{par|{{spar|item-declaration}}|a declaration for each range item}}
{{par|{{spar|range-expression}}|any {{rlp|expressions|expression}} that represents a suitable sequence (either an array or an object for which {{tt|begin}} and {{tt|end}} member functions or free functions are defined, see below) or a {{rlp|list initialization|braced-init-list}}. }}
+
{{par|{{spar|range-initializer}}|an {{rlp|expressions|expression}} or {{rlp|initialization|brace-enclosed initializer list}}}}
{{par|{{spar|loop-statement}}|any {{rlp|statements|statement}}, typically a compound statement, which is the body of the loop }}
+
{{par|{{spar|statement}}|any {{rlp|statements|statement}} (typically a compound statement)}}
 
{{par end}}
 
{{par end}}
 
 
{{rrev|since=c++17|
 
{{spar|range-declaration}} may be a {{rlp|structured binding|structured binding declaration}}:
 
{{source|1=
 
for (auto&& [first, second] : mymap)
 
{
 
    // use first and second
 
}
 
}}
 
}}
 
  
 
===Explanation===
 
===Explanation===
The above syntax produces code similar to the following. The variables '''{{tt|__range}}''', '''{{tt|__begin}}''' and '''{{tt|__end}}''' are for exposition only. The code is not equivalent, because it does not take into account the lifetime expansion of temporaries of {{spar|range-expression}}.
+
The above syntax produces code equivalent to the following {{rev inl|since=c++23| except for the lifetime expansion of temporaries of {{spar|range-initializer}} (see [[#Temporary range initializer|below]])}} (the variables and expressions wrapped in {{c/core|/* */}} are for exposition only):
  
 
{{rev begin}}
 
{{rev begin}}
 
{{rev|until=c++17|
 
{{rev|until=c++17|
 
{{ttb|{}}<br>
 
{{ttb|{}}<br>
:{{ttb|auto &amp;&amp; __range {{=}}}} {{spar|range-expression}} {{ttb|;}}<br>
+
:{{ttb|auto&&}} {{c/core|/* range */}} {{ttb|1==}} {{spar sep|range-initializer}}{{ttb|;}}<br>
:{{ttb|for (auto __begin {{=}}}} ''begin-expr''{{ttb|,}} {{ttb|__end {{=}}}} ''end-expr''{{ttb|;}} {{ttb|__begin !{{=}} __end; ++__begin)}}<br>
+
:{{ttb|for (auto}} {{c/core|/* begin */}} {{ttb|1==}} {{c/core|/* begin-expr */}}{{ttb|,}} {{c/core|/* end */}} {{ttb|1==}} {{c/core|/* end-expr */}}{{ttb|;}}<br>
 +
:::{{c/core|/* begin */}} {{ttb|1=!=}} {{c/core|/* end */}}{{ttb|; ++}}{{c/core|/* begin */}}{{ttb|)}}<br>
 
:{{ttb|{}}
 
:{{ttb|{}}
::{{spar|range-declaration}} {{ttb|{{=}} *__begin;}}<br>
+
::{{spar|item-declaration}} {{ttb|1== *}}{{c/core|/* begin */}}{{ttb|;}}<br>
::{{spar|loop-statement}}<br>
+
::{{spar|statement}}<br>
:{{ttb|}<!-- -->}}<br>
+
:{{ttb|}<!---->}}<br>
{{ttb|}<!-- -->}}<br>
+
{{ttb|}<!---->}}<br>
 
}}
 
}}
 
{{rev|since=c++17|until=c++20|
 
{{rev|since=c++17|until=c++20|
 
{{ttb|{}}<br>
 
{{ttb|{}}<br>
:{{ttb|auto &amp;&amp; __range {{=}}}} {{spar|range-expression}} {{ttb|;}}<br>
+
:{{ttb|auto&&}} {{c/core|/* range */}} {{ttb|1==}} {{spar sep|range-initializer}}{{ttb|;}}<br>
:{{ttb|auto __begin {{=}}}} ''begin-expr'' {{ttb|;}}<br>
+
:{{ttb|auto}} {{c/core|/* begin */}} {{ttb|1==}} {{c/core|/* begin-expr */}}{{ttb|;}}<br>
:{{ttb|auto __end {{=}}}} ''end-expr'' {{ttb|;}}<br>
+
:{{ttb|auto}} {{c/core|/* end */}} {{ttb|1==}} {{c/core|/* end-expr */}}{{ttb|;}}<br>
:{{ttb|for ( ; __begin !{{=}} __end; ++__begin)}}<br>
+
:{{ttb|for ( ;}} {{c/core|/* begin */}} {{ttb|1=!=}} {{c/core|/* end */}}{{ttb|; ++}}{{c/core|/* begin */}}{{ttb|)}}<br>
 
:{{ttb|{}}
 
:{{ttb|{}}
::{{spar|range-declaration}} {{ttb|{{=}} *__begin;}}<br>
+
::{{spar|item-declaration}} {{ttb|1== *}}{{c/core|/* begin */}}{{ttb|;}}<br>
::{{spar|loop-statement}}<br>
+
::{{spar|statement}}<br>
:{{ttb|}<!-- -->}}<br>
+
:{{ttb|}<!---->}}<br>
{{ttb|}<!-- -->}}<br>
+
{{ttb|}<!---->}}<br>
 
}}
 
}}
 
{{rev|since=c++20|
 
{{rev|since=c++20|
 
{{ttb|{}}<br>
 
{{ttb|{}}<br>
 
:{{spar|init-statement}}<br>
 
:{{spar|init-statement}}<br>
:{{ttb|auto &amp;&amp; __range {{=}}}} {{spar|range-expression}} {{ttb|;}}<br>
+
:{{ttb|auto&&}} {{c/core|/* range */}} {{ttb|1==}} {{spar sep|range-initializer}}{{ttb|;}}<br>
:{{ttb|auto __begin {{=}}}} ''begin-expr'' {{ttb|;}}<br>
+
:{{ttb|auto}} {{c/core|/* begin */}} {{ttb|1==}} {{c/core|/* begin-expr */}}{{ttb|;}}<br>
:{{ttb|auto __end {{=}}}} ''end-expr'' {{ttb|;}}<br>
+
:{{ttb|auto}} {{c/core|/* end */}} {{ttb|1==}} {{c/core|/* end-expr */}}{{ttb|;}}<br>
:{{ttb|for ( ; __begin !{{=}} __end; ++__begin)}}<br>
+
:{{ttb|for ( ;}} {{c/core|/* begin */}} {{ttb|1=!=}} {{c/core|/* end */}}{{ttb|; ++}}{{c/core|/* begin */}}{{ttb|)}}<br>
 
:{{ttb|{}}
 
:{{ttb|{}}
::{{spar|range-declaration}} {{ttb|{{=}} *__begin;}}<br>
+
::{{spar|item-declaration}} {{ttb|1== *}}{{c/core|/* begin */}}{{ttb|;}}<br>
::{{spar|loop-statement}}<br>
+
::{{spar|statement}}<br>
:{{ttb|}<!-- -->}}<br>
+
:{{ttb|}<!---->}}<br>
{{ttb|}<!-- -->}}<br>
+
{{ttb|}<!---->}}<br>
 
}}
 
}}
 
{{rev end}}
 
{{rev end}}
  
{{spar|range-expression}} is evaluated to determine the sequence or range to iterate. Each element of the sequence, in turn, is dereferenced and is used to initialize the variable with the type and name given in {{spar|range-declaration}}.
+
{{spar|range-initializer}} is evaluated to initialize the sequence or range to iterate. Each element of the sequence, in turn, is dereferenced and is used to initialize the variable with the type and name given in {{spar|item-declaration}}.
  
''{{tt|begin-expr}}'' and ''{{tt|end-expr}}'' are defined as follows:
+
{{spar|item-declaration}} can be one of the following:
* If {{spar|range-expression}} is an expression of array type, then ''{{tt|begin-expr}}'' is {{c|__range}} and ''{{tt|end-expr}}'' is {{c|(__range + __bound)}}, where {{tt|__bound}} is the number of elements in the array (if the array has unknown size or is of an incomplete type, the program is ill-formed)
+
* a {{rlpsd|declarations#Simple declaration}} with the following restrictions:
* If {{spar|range-expression}} is an expression of a class type {{tt|C}} that has both a member named {{c|begin}} and a member named {{c|end}} (regardless of the type or accessibility of such member), then ''{{tt|begin-expr}}'' is {{c|__range.begin()}} and ''{{tt|end-expr}}'' is {{c|__range.end()}};
+
* It has only one {{rlp|declarations#Declarators|declarator}}.
* Otherwise, ''{{tt|begin-expr}}'' is {{c|begin(__range)}} and ''{{tt|end-expr}}'' is {{c|end(__range)}}, which are found via {{rlp|adl|argument-dependent lookup}} (non-ADL lookup is not performed).
+
* The declarator must have no {{rlp|initialization|initializer}}.
 +
* The {{rlp|declarations#Specifiers|declaration specifier sequence}} can only contain type specifiers and {{c/core|constexpr}}, and it cannot define a {{rlp|class}} or {{rlp|enum|enumeration}}.
  
Just as with a traditional loop, a {{rlp|break|break statement}} can be used to exit the loop early and a {{rlp|continue|continue statement}} can be used to restart the loop with the next element.
+
Exposition-only expressions {{c/core|/* begin-expr */}} and {{c/core|/* end-expr */}} are defined as follows:
 +
* If the type of {{c/core|/* range */}} is a reference to an array type {{tt|R}}:
 +
:* If {{tt|R}} is of bound {{c|N}}, {{c/core|/* begin-expr */}} is {{c|/* range */}} and {{c/core|/* end-expr */}} is {{c|/* range */ + N}}.
 +
:* If {{tt|R}} is an array of unknown bound or an array of incomplete type, the program is ill-formed.
 +
* If the type of {{c/core|/* range */}} is a reference to a class type {{tt|C}}, and searches in the scope of {{tt|C}} for the names “{{tt|begin}}” and “{{tt|end}}” each find at least one declaration, then {{c/core|/* begin-expr */}} is {{c|/* range */.begin()}} and {{c/core|/* end-expr */}} is {{c|/* range */.end()}}.
 +
* Otherwise, {{c/core|/* begin-expr */}} is {{c|begin(/* range */)}} and {{c/core|/* end-expr */}} is {{c|end(/* range */)}}, where “{{tt|begin}}” and “{{tt|end}}” are found via {{rlp|adl|argument-dependent lookup}} (non-ADL lookup is not performed).
  
If a name introduced in {{spar|init-statement}} is redeclared in the outermost block of {{spar|loop-statement}}, the program is ill-formed:
+
If the loop needs to be terminated within {{spar|statement}}, a {{rlp|break|{{c/core|break}} statement}} can be used as terminating statement.
 +
 
 +
If the current iteration needs to be terminated within {{spar|statement}}, a {{rlp|continue|{{c/core|continue}} statement}} can be used as shortcut.
 +
 
 +
If a name introduced in {{spar|init-statement}} is redeclared in the outermost block of {{spar|statement}}, the program is ill-formed:
 
{{source|1=
 
{{source|1=
for (int i : { 1, 2, 3 })
+
for (int i : {1, 2, 3})
 
     int i = 1; // error: redeclaration
 
     int i = 1; // error: redeclaration
 
}}
 
}}
  
===Temporary range expression===
+
===Temporary range initializer===
If {{spar|range-expression}} returns a temporary, its lifetime is extended until the end of the loop, as indicated by binding to the forwarding reference '''{{tt|__range}}'''. Lifetimes of all temporaries within {{spar|range-expression}} are {{rev inl|until=c++23|''not''}} extended {{rev inl|since=c++23|if they would otherwise be destroyed at the end of {{spar|range-expression}}}}.
+
If {{spar|range-initializer}} returns a temporary, its lifetime is extended until the end of the loop, as indicated by binding to the forwarding reference {{c/core|/* range */}}.
{{source|1=
+
 
 +
Lifetimes of all temporaries within {{spar|range-initializer}} are not extended{{rev inl|since=c++23| unless they would otherwise be destroyed at the end of {{spar|range-initializer}}}}.
 +
{{source|
 
// if foo() returns by value
 
// if foo() returns by value
for (auto& x : foo().items()) { /* .. */ } // until C++23 undefined behavior   
+
for (auto& x : foo().items()) { /* ... */ } // until C++23 undefined behavior   
 
}}
 
}}
 
{{rrev|since=c++20|This problem may be worked around using {{spar|init-statement}}:
 
{{rrev|since=c++20|This problem may be worked around using {{spar|init-statement}}:
Line 104: Line 105:
 
}}
 
}}
  
{{rrev|since=c++23|Note that even in C++23 non-reference parameters of intermediate function calls do not get a lifetime extension (because in some ABIs they are destroyed in the callee, not in the caller), but that is only a problem for functions that are buggy anyway:
+
 
 +
{{rrev|since=c++23|
 +
Note that even in C++23 non-reference parameters of intermediate function calls do not get a lifetime extension (because in some ABIs they are destroyed in the callee, not in the caller), but that is only a problem for functions that are buggy anyway:
 
{{source|1=
 
{{source|1=
 
using T = std::list<int>;
 
using T = std::list<int>;
Line 113: Line 116:
 
void foo()
 
void foo()
 
{
 
{
     for (auto e : f1(g())) {} // OK: lifetime of return value of g() extended
+
     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
+
     for (auto e : f2(g())) {} // UB: lifetime of f2's value parameter ends early
 
}
 
}
 
}}
 
}}
Line 120: Line 123:
  
 
===Notes===
 
===Notes===
If the initializer ({{spar|range-expression}}) is a braced-init-list, '''{{tt|__range}}''' is deduced to be {{c|std::initializer_list<>&&}}.
+
If the {{spar|range-initializer}} is a {{rlp|initialization|braced-enclosed initializer list}}, {{c/core|/* range */}} is deduced to be a reference to a {{lc|std::initializer_list}}.
  
 
It is safe, and in fact, preferable in generic code, to use deduction to forwarding reference, {{c|for (auto&& var : sequence)}}.
 
It is safe, and in fact, preferable in generic code, to use deduction to forwarding reference, {{c|for (auto&& var : sequence)}}.
  
The member interpretation is used if the range type has a member named {{tt|begin}} and a member named {{tt|end}}. This is done regardless of whether the member is a type, data member, function, or enumerator, and regardless of its accessibility. Thus a class like {{c|1=class meow { enum { begin = 1, end = 2 }; /* rest of class */ };}} cannot be used with the range-based for loop even if the namespace-scope begin/end functions are present.  
+
The member interpretation is used if the range type has a member named {{tt|begin}}and a member named {{tt|end}}. This is done regardless of whether the member is a type, data member, function, or enumerator, and regardless of its accessibility. Thus a class like {{c|1=class meow { enum { begin = 1, end = 2 }; /* rest of class */ };}} cannot be used with the range-based {{c/core|for}} loop even if the namespace-scope “{{tt|begin}}”/“{{tt|end}}” functions are present.  
  
While the variable declared in the {{spar|range-declaration}} is usually used in the {{spar|loop-statement}}, doing so is not required.
+
While the variable declared in the {{spar|item-declaration}} is usually used in the {{spar|statement}}, doing so is not required.
  
 
{{rrev|since=c++17|
 
{{rrev|since=c++17|
As of C++17, the types of the ''{{tt|begin-expr}}'' and the ''{{tt|end-expr}}'' do not have to be the same, and in fact the type of the ''{{tt|end-expr}}'' does not have to be an iterator: it just needs to be able to be compared for inequality with one. This makes it possible to delimit a range by a predicate (e.g. "the iterator points at a null character").}}
+
As of C++17, the types of the {{c/core|/* begin-expr */}} and the {{c/core|/* end-expr */}} do not have to be the same, and in fact the type of the {{c/core|/* end-expr */}} does not have to be an iterator: it just needs to be able to be compared for inequality with one. This makes it possible to delimit a range by a predicate (e.g. "the iterator points at a null character").}}
  
When used with a (non-const) object that has copy-on-write semantics, the range-based for loop may trigger a deep copy by (implicitly) calling the non-const {{tt|begin()}} member function.
+
When used with a (non-const) object that has copy-on-write semantics, the range-based {{c/core|for}} loop may trigger a deep copy by (implicitly) calling the non-const {{tt|begin()}} member function.
  
{{rrev|since=c++17|If that is undesirable (for instance because the loop is not actually modifying the object), it can be avoided by using {{lc|std::as_const}}:
+
{{rrev|since=c++17|
 +
If that is undesirable (for instance because the loop is not actually modifying the object), it can be avoided by using {{lc|std::as_const}}:
  
 
{{source|1=
 
{{source|1=
Line 146: Line 150:
  
 
{{ftm begin|core=1|std=1|comment=1}}
 
{{ftm begin|core=1|std=1|comment=1}}
{{ftm|value=200907L|std=C++11|__cpp_range_based_for|rowspan="3"|[[#Top|Range-based {{tt|for}} loop]]}}
+
{{ftm|value=200907L|std=C++11|__cpp_range_based_for|rowspan="3"|[[#top|Range-based {{c/core|for}} loop]]}}
{{ftm|value=201603L|std=C++17|-|Range-based {{tt|for}} loop with [[#Explanation|different {{tt|begin}}/{{tt|end}}]] types}}
+
{{ftm|value=201603L|std=C++17|-|Range-based {{c/core|for}} loop with [[#Explanation|different {{tt|begin}}/{{tt|end}}]] types}}
{{ftm|value=2022??L|std=C++23|-|{{todo|lifetime extension.}}}}
+
{{ftm|value=202211L|std=C++23|-|Lifetime extension for all temporary objects in {{spar|range-initializer}}}}
 
{{ftm end}}
 
{{ftm end}}
  
Line 163: Line 167:
 
{
 
{
 
     std::vector<int> v = {0, 1, 2, 3, 4, 5};
 
     std::vector<int> v = {0, 1, 2, 3, 4, 5};
 
+
   
 
     for (const int& i : v) // access by const reference
 
     for (const int& i : v) // access by const reference
 
         std::cout << i << ' ';
 
         std::cout << i << ' ';
Line 171: Line 175:
 
         std::cout << i << ' ';
 
         std::cout << i << ' ';
 
     std::cout << '\n';
 
     std::cout << '\n';
 
+
   
 
     for (auto&& i : v) // access by forwarding reference, the type of i is int&
 
     for (auto&& i : v) // access by forwarding reference, the type of i is int&
 
         std::cout << i << ' ';
 
         std::cout << i << ' ';
 
     std::cout << '\n';
 
     std::cout << '\n';
 
+
   
 
     const auto& cv = v;
 
     const auto& cv = v;
 
+
   
 
     for (auto&& i : cv) // access by f-d reference, the type of i is const int&
 
     for (auto&& i : cv) // access by f-d reference, the type of i is const int&
 
         std::cout << i << ' ';
 
         std::cout << i << ' ';
 
     std::cout << '\n';
 
     std::cout << '\n';
 
+
   
     for (int n : {0, 1, 2, 3, 4, 5}) // the initializer may be a braced-init-list
+
     for (int n : {0, 1, 2, 3, 4, 5}) // the initializer may be a
 +
                                    // braced-enclosed initializer list
 
         std::cout << n << ' ';
 
         std::cout << n << ' ';
 
     std::cout << '\n';
 
     std::cout << '\n';
 
+
   
 
     int a[] = {0, 1, 2, 3, 4, 5};
 
     int a[] = {0, 1, 2, 3, 4, 5};
 
     for (int n : a) // the initializer may be an array
 
     for (int n : a) // the initializer may be an array
 
         std::cout << n << ' ';
 
         std::cout << n << ' ';
 
     std::cout << '\n';
 
     std::cout << '\n';
 
+
   
 
     for ([[maybe_unused]] int n : a)   
 
     for ([[maybe_unused]] int n : a)   
 
         std::cout << 1 << ' '; // the loop variable need not be used
 
         std::cout << 1 << ' '; // the loop variable need not be used
 
     std::cout << '\n';
 
     std::cout << '\n';
 
+
   
 
     for (auto n = v.size(); auto i : v) // the init-statement (C++20)
 
     for (auto n = v.size(); auto i : v) // the init-statement (C++20)
 
         std::cout << --n + i << ' ';
 
         std::cout << --n + i << ' ';
 
     std::cout << '\n';
 
     std::cout << '\n';
 
+
   
 
     for (typedef decltype(v)::value_type elem_t; elem_t i : v)
 
     for (typedef decltype(v)::value_type elem_t; elem_t i : v)
 
     // typedef declaration as init-statement (C++20)
 
     // typedef declaration as init-statement (C++20)
 
         std::cout << i << ' ';
 
         std::cout << i << ' ';
 
     std::cout << '\n';
 
     std::cout << '\n';
 
+
   
 
     for (using elem_t = decltype(v)::value_type; elem_t i : v)
 
     for (using elem_t = decltype(v)::value_type; elem_t i : v)
 
     // alias declaration as init-statement (C++23)
 
     // alias declaration as init-statement (C++23)
Line 224: Line 229:
 
===Defect reports===
 
===Defect reports===
 
{{dr list begin}}
 
{{dr list begin}}
{{dr list item|wg=cwg|dr=1442|std=C++11|before=it was unspecified whether the lookup of non-member<br>{{tt|begin}} and {{tt|end}} includes usual unqualified lookup|after=no usual unqualified lookup}}
+
{{dr list item|wg=cwg|dr=1442|std=C++11|before=it was unspecified whether the lookup of non-member<br>{{tt|begin}}and {{tt|end}}includes usual unqualified lookup|after=no usual unqualified lookup}}
 
{{dr list item|wg=cwg|dr=2220|std=C++11|before=the names introduced in {{spar|init-statement}} could be redeclared|after=the program is ill-formed in this case}}
 
{{dr list item|wg=cwg|dr=2220|std=C++11|before=the names introduced in {{spar|init-statement}} could be redeclared|after=the program is ill-formed in this case}}
{{dr list item|paper=P0962R1|std=C++11|before=member interpretation was used if<br>either member {{tt|begin}} and {{tt|end}} is present|after=only used if both are present}}
+
{{dr list item|wg=cwg|dr=2825|std=C++11|before=if {{spar|range-initializer}} is a brace-enclosed initializer list,<br>the non-member “{{tt|begin}}” and “{{tt|end}}” will be looked up|after=will lookup member “{{tt|begin}}”<br>and “{{tt|end}}” in this case}}
 +
{{dr list item|paper=P0962R1|std=C++11|before=member interpretation was used if either<br>member {{tt|begin}}and {{tt|end}}is present|after=only used if both are present}}
 
{{dr list end}}
 
{{dr list end}}
  

Latest revision as of 23:24, 19 June 2024

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

Executes a for loop over a range.

Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.

Contents

[edit] Syntax

attr (optional) for ( init-statement (optional) item-declaration : range-initializer ) statement
attr - any number of attributes
init-statement - (since C++20) one of
(since C++23)

Note that any init-statement must end with a semicolon. This is why it is often described informally as an expression or a declaration followed by a semicolon.

item-declaration - a declaration for each range item
range-initializer - an expression or brace-enclosed initializer list
statement - any statement (typically a compound statement)

[edit] Explanation

The above syntax produces code equivalent to the following except for the lifetime expansion of temporaries of range-initializer (see below)(since C++23) (the variables and expressions wrapped in /* */ are for exposition only):

{

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

}

(until C++17)

{

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

}

(since C++17)
(until C++20)

{

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

}

(since C++20)

range-initializer is evaluated to initialize the sequence or range to iterate. Each element of the sequence, in turn, is dereferenced and is used to initialize the variable with the type and name given in item-declaration.

item-declaration can be one of the following:

Exposition-only expressions /* begin-expr */ and /* end-expr */ are defined as follows:

  • If the type of /* range */ is a reference to an array type R:
  • If R is of bound N, /* begin-expr */ is /* range */ and /* end-expr */ is /* range */ + N.
  • If R is an array of unknown bound or an array of incomplete type, the program is ill-formed.
  • If the type of /* range */ is a reference to a class type C, and searches in the scope of C for the names “begin” and “end” each find at least one declaration, then /* begin-expr */ is /* range */.begin() and /* end-expr */ is /* range */.end().
  • Otherwise, /* begin-expr */ is begin(/* range */) and /* end-expr */ is end(/* range */), where “begin” and “end” are found via argument-dependent lookup (non-ADL lookup is not performed).

If the loop needs to be terminated within statement, a break statement can be used as terminating statement.

If the current iteration needs to be terminated within statement, a continue statement can be used as shortcut.

If a name introduced in init-statement is redeclared in the outermost block of statement, the program is ill-formed:

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

[edit] Temporary range initializer

If range-initializer returns a temporary, its lifetime is extended until the end of the loop, as indicated by binding to the forwarding reference /* range */.

Lifetimes of all temporaries within range-initializer are not extended unless they would otherwise be destroyed at the end of range-initializer(since C++23).

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

This problem may be worked around using init-statement:

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


Note that even in C++23 non-reference parameters of intermediate function calls do not get a lifetime extension (because in some ABIs they are destroyed in the callee, not in the caller), but that is only a problem for functions that are buggy anyway:

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
}
(since C++23)

[edit] Notes

If the range-initializer is a braced-enclosed initializer list, /* range */ is deduced to be a reference to a std::initializer_list.

It is safe, and in fact, preferable in generic code, to use deduction to forwarding reference, for (auto&& var : sequence).

The member interpretation is used if the range type has a member named “begin” and a member named “end”. This is done regardless of whether the member is a type, data member, function, or enumerator, and regardless of its accessibility. Thus a class like class meow { enum { begin = 1, end = 2 }; /* rest of class */ }; cannot be used with the range-based for loop even if the namespace-scope “begin”/“end” functions are present.

While the variable declared in the item-declaration is usually used in the statement, doing so is not required.

As of C++17, the types of the /* begin-expr */ and the /* end-expr */ do not have to be the same, and in fact the type of the /* end-expr */ does not have to be an iterator: it just needs to be able to be compared for inequality with one. This makes it possible to delimit a range by a predicate (e.g. "the iterator points at a null character").

(since C++17)

When used with a (non-const) object that has copy-on-write semantics, the range-based for loop may trigger a deep copy by (implicitly) calling the non-const begin() member function.

If that is undesirable (for instance because the loop is not actually modifying the object), it can be avoided by using 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)) { /* ... */ }
(since C++17)
Feature-test macro Value Std Feature
__cpp_range_based_for 200907L (C++11) Range-based for loop
201603L (C++17) Range-based for loop with different begin/end types
202211L (C++23) Lifetime extension for all temporary objects in range-initializer

[edit] Keywords

for

[edit] Example

#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';
}

Output:

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

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 1442 C++11 it was unspecified whether the lookup of non-member
begin” and “end” includes usual unqualified lookup
no usual unqualified lookup
CWG 2220 C++11 the names introduced in init-statement could be redeclared the program is ill-formed in this case
CWG 2825 C++11 if range-initializer is a brace-enclosed initializer list,
the non-member “begin” and “end” will be looked up
will lookup member “begin
and “end” in this case
P0962R1 C++11 member interpretation was used if either
member “begin” and “end” is present
only used if both are present

[edit] See also

applies a function to a range of elements
(function template) [edit]