Range-based for loop on a dynamic array?
You can't use range-for-loop with dynamically allocated arrays, since compiler can't deduce begin and end of this array. You should always use containers instead of it, for example std::vector
.
std::vector<int> v(size);
for(const auto& elem: v)
// do something
C++20 adds std::span
, which allows looping like this:
#include <iostream>
#include <span>
int main () {
auto p = new int[5];
for (auto &v : std::span(p, 5)) {
v = 1;
}
for (auto v : std::span(p, 5)) {
std::cout << v << '\n';
}
delete[] p;
}
This is supported by current compilers, e.g. gcc 10.1 and clang 7.0.0 and later. (Live)
Of course, if you have the choice, it is preferable to use std::vector
over C-style arrays from the get-go.
To make use of the range-based for-loop you have to provide either begin()
and end()
member functions or overload the non-member begin()
and end()
functions.
In the latter case, you can wrap your range in a std::pair
and overload begin()
and end()
for those:
namespace std {
template <typename T> T* begin(std::pair<T*, T*> const& p)
{ return p.first; }
template <typename T> T* end(std::pair<T*, T*> const& p)
{ return p.second; }
}
Now you can use the for-loop like this:
for (auto&& i : std::make_pair(array, array + size))
cout << i << endl;
Note, that the non-member begin()
and end()
functions have to be overloaded in the std
namespace here, because pair
also resides in namespace std
. If you don't feel like tampering with the standard namespace, you can simply create your own tiny pair class and overload begin()
and end()
in your namespace.
Or, create a thin wrapper around your dynamically allocated array and provide begin()
and end()
member functions:
template <typename T>
struct wrapped_array {
wrapped_array(T* first, T* last) : begin_ {first}, end_ {last} {}
wrapped_array(T* first, std::ptrdiff_t size)
: wrapped_array {first, first + size} {}
T* begin() const noexcept { return begin_; }
T* end() const noexcept { return end_; }
T* begin_;
T* end_;
};
template <typename T>
wrapped_array<T> wrap_array(T* first, std::ptrdiff_t size) noexcept
{ return {first, size}; }
And your call site looks like this:
for (auto&& i : wrap_array(array, size))
std::cout << i << std::endl;
Example
You can't perform a range based loop directly over a dynamically allocated array because all you have is a pointer to the first element. There is no information concerning its size that the compiler can use to perform the loop. The idiomatic C++ solution would be to replace the dynamically allocated array by an std::vector
:
std::vector<int> arr(size);
for(const auto& i : arr)
std::cout<< i << std::endl;
Alternatively, you could use a range type that provides a begin and end iterator based on a pointer and an offset. Have a look at some of the types in the boost.range library, or at the GSL span proposal (example implementation here, reference for C++20 proposed type here).
Note that a range based for loop does work for std::array
objects of fixes size plain arrays:
std::array<int,10> arr;
for(const auto& i : arr)
std::cout<< i << std::endl;
int arr[10] = .... ;
for(const auto& i : arr)
std::cout<< i << std::endl;
but in both cases the size needs to be a compile-time constant.