Range-Based For
suggest changefor
loops can be used to iterate over the elements of a iterator-based range, without using a numeric index or directly accessing the iterators:
vector<float> v = {0.4f, 12.5f, 16.234f};
for (auto val: v)
{
std::cout << val << " ";
}
std::cout << std::endl;
This will iterate over every element in v
, with val
getting the value of the current element. The following statement:
for (for-range-declaration : for-range-initializer ) statement
is equivalent to:
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr, __end = end-expr;
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
In C++ 17:
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // end is allowed to be a different type than begin in C++17
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
This change was introduced for the planned support of Ranges TS in C++20.
In this case, our loop is equivalent to:
{
auto&& __range = v;
auto __begin = v.begin(), __end = v.end();
for (; __begin != __end; ++__begin) {
auto val = *__begin;
std::cout << val << " ";
}
}
Note that auto val
declares a value type, which will be a copy of a value stored in the range (we are copy-initializing it from the iterator as we go). If the values stored in the range are expensive to copy, you may want to use const auto &val
. You are also not required to use auto
; you can use an appropriate typename, so long as it is implicitly convertible from the range’s value type.
If you need access to the iterator, range-based for cannot help you (not without some effort, at least).
If you wish to reference it, you may do so:
vector<float> v = {0.4f, 12.5f, 16.234f};
for(float &val: v)
{
std::cout << val << " ";
}
You could iterate on const
reference if you have const
container:
const vector<float> v = {0.4f, 12.5f, 16.234f};
for(const float &val: v)
{
std::cout << val << " ";
}
One would use forwarding references when the sequence iterator returns a proxy object and you need to operate on that object in a non-const
way. Note: it will most likely confuse readers of your code.
vector<bool> v(10);
for(auto&& val: v)
{
val = true;
}
The “range” type provided to range-based for
can be one of the following:
- Language arrays:
float arr[] = {0.4f, 12.5f, 16.234f}; for(auto val: arr) { std::cout << val << " "; } Note that allocating a dynamic array does not count: float *arr = new float[3]{0.4f, 12.5f, 16.234f}; for(auto val: arr) //Compile error. { std::cout << val << " "; }
- Any type which has member functions
begin()
andend()
, which return iterators to the elements of the type. The standard library containers qualify, but user-defined types can be used as well:struct Rng { float arr[3]; // pointers are iterators const float* begin() const {return &arr[0];} const float* end() const {return &arr[3];} float* begin() {return &arr[0];} float* end() {return &arr[3];} }; int main() { Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
- Any type which has non-member
begin(type)
andend(type)
functions which can found via argument dependent lookup, based ontype
. This is useful for creating a range type without having to modify class type itself:namespace Mine { struct Rng {float arr[3];}; // pointers are iterators const float* begin(const Rng &rng) {return &rng.arr[0];} const float* end(const Rng &rng) {return &rng.arr[3];} float* begin(Rng &rng) {return &rng.arr[0];} float* end(Rng &rng) {return &rng.arr[3];} } int main() { Mine::Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }