for
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
}
}
{
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()
and end()
, 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)
and end(type)
functions which can found via argument dependent lookup, based on type
. 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 << " ";
}
}