Range-based for loop (since C++11)

Executes a for loop over a range.

# 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”).

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:

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

# Defect reports

DRApplied toBehavior as publishedCorrect behavior
CWG 1442C++11it was unspecified whether the lookup of non-member“begin” and “end” includes usual unqualified lookupno usual unqualified lookup
CWG 2220C++11the names introduced in init-statement could be redeclaredthe program is ill-formed in this case
CWG 2825C++11if range-initializer is a brace-enclosed initializer list,the non-member “begin” and “end” will be looked upwill lookup member “begin”and “end” in this case
P0962R1C++11member interpretation was used if eithermember “begin” and “end” is presentonly used if both are present

# See also