Difference between revisions of "cpp/language/range-for"
(Add example for P2360R0 (although its usage is weird IMO).) |
m (→Example: let it be compilable now.) |
||
Line 172: | Line 172: | ||
std::cout << '\n'; | std::cout << '\n'; | ||
+ | # if __cplusplus > 23'00 + 200000 | ||
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, same as above (C++23) | // alias declaration as init-statement, same as above (C++23) | ||
std::cout << i << ' '; | std::cout << i << ' '; | ||
std::cout << '\n'; | std::cout << '\n'; | ||
+ | # endif | ||
} | } | ||
+ | | p=true | ||
| output= | | output= | ||
0 1 2 3 4 5 | 0 1 2 3 4 5 |
Revision as of 05:55, 11 October 2021
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 |
Syntax
attr(optional) for ( init-statement(optional)range-declaration : range-expression )
loop-statement |
|||||||||
attr | - | any number of attributes | ||
init-statement | - | (since C++20) either
| ||
range-declaration | - | a declaration of a named variable, whose type is the type of the element of the sequence represented by range-expression, or a reference to that type. Often uses the auto specifier for automatic type deduction | ||
range-expression | - | any expression that represents a suitable sequence (either an array or an object for which begin and end member functions or free functions are defined, see below) or a braced-init-list.
| ||
loop-statement | - | any statement, typically a compound statement, which is the body of the loop |
range-declaration may be a structured binding declaration for (auto&& [first,second] : mymap) { // use first and second } |
(since C++17) |
Explanation
The above syntax produces code equivalent to the following (__range
, __begin
and __end
are for exposition only):
|
(until C++17) |
|
(since C++17) (until C++20) |
|
(since C++20) |
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 range-declaration.
begin_expr
and end_expr
are defined as follows:
- If range-expression is an expression of array type, then
begin_expr
is __range andend_expr
is (__range + __bound), where__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) - If range-expression is an expression of a class type
C
that has both a member named begin and a member named end (regardless of the type or accessibility of such member), thenbegin_expr
is __range.begin() andend_expr
is __range.end(); - Otherwise,
begin_expr
is begin(__range) andend_expr
is end(__range), which are found via argument-dependent lookup (non-ADL lookup is not performed).
Just as with a traditional loop, a break statement can be used to exit the loop early and a continue statement can be used to restart the loop with the next element.
Temporary range expression
If range-expression returns a temporary, its lifetime is extended until the end of the loop, as indicated by binding to the forwarding reference __range
, but beware that the lifetime of any temporary within range-expression is not extended.
for (auto& x : foo().items()) { /* .. */ } // undefined behavior if foo() returns by value
This problem may be worked around using init-statement: for (T thing = foo(); auto& x : thing.items()) { /* ... */ } // OK |
(since C++20) |
Notes
If the initializer (range-expression) is a braced-init-list, __range is deduced to be 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 range-declaration is usually used in the loop-statement, doing so is not required.
As of C++17, the types of the |
(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) |
Keywords
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-init-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'; # if __cplusplus > 23'00 + 200000 for (using elem_t = decltype(v)::value_type; elem_t i : v) // alias declaration as init-statement, same as above (C++23) std::cout << i << ' '; std::cout << '\n'; # endif }
Possible 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
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
P0962R1 | C++11 | member interpretation is used if either member begin and end is present | only used if both are present |
See also
applies a function to a range of elements (function template) |