Range-Based for Loop on a Dynamic Array

Range-based for loop on a dynamic array?

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

Why doesn't C++ support range based for loop for dynamic arrays?

int* array = new int[len];
for[] (int i : array) {}

There are several points which must be addressed; I'll tackle them one at a time.

Does the run-time knows the size of the array?

In certain conditions, it must. As you pointed out, a call to delete[] will call the destructor of each element (in reserve order) and therefore must know how many there are.

However, by not specifying that the number of elements must be known, and accessible, the C++ standard allows an implementation to omit it whenever the call to the destructor is not required (std::is_trivially_destructible<T>::value evaluates to true).

Can the run-time distinguish between pointer and array?

In general, no.

When you have a pointer, it could point to anything:

  • a single item, or an item in an array,
  • the first item in an array, or any other,
  • an array on the stack, or an array on the heap,
  • just an array, or an array part of a larger object.

This is the reason what delete[] exists, and using delete here would be incorrect. With delete[], you the user state: this pointer points to the first item of a heap-allocated array.

The implementation can then assume that, for example, in the 8 bytes preceding this first item it can find the size of the array. Without you guaranteeing this, those 8 bytes could be anything.

Then, why not go all the way and create for[] (int i : array)?

There are two reasons:

  1. As mentioned, today an implementation can elide the size on a number of elements; with this new for[] syntax, it would no longer be possible on a per-type basis.
  2. It's not worth it.

Let us be honest, new[] and delete[] are relics of an older time. They are incredibly awkward:

  • the number of elements has to be known in advance, and cannot be changed,
  • the elements must be default constructible, or otherwise C-ish,

and unsafe to use:

  • the number of elements is inaccessible to the user.

There is generally no reason to use new[] and delete[] in modern C++. Most of the times a std::vector should be preferred; in the few instances where the capacity is superfluous, a std::dynarray is still better (because it keeps track of the size).

Therefore, without a valid reason to keep using these statements, there is no motivation to include new semantic constructs specifically dedicated to handling them.

And should anyone be motivated enough to make such a proposal:

  • the inhibition of the current optimization, a violation of C++ philosophy of "You don't pay for what you don't use", would likely be held against them,
  • the inclusion of new syntax, when modern C++ proposals have gone to great lengths to avoid it as much as possible (to the point of having a library defined std::variant), would also likely be held against them.

I recommend that you simply use std::vector.

C++11 Using Range-based for loop (for each) for dynamic array

Just use a container-like wrapper:

template <typename T>
struct Wrapper
{
T* ptr;
std::size_t length;
};

template <typename T>
Wrapper<T> make_wrapper(T* ptr, std::size_t len) {return {ptr, len};}

template <typename T>
T* begin(Wrapper<T> w) {return w.ptr;}

template <typename T>
T* end(Wrapper<T> w) {return begin(w) + w.length;}

Usage:

for (auto i : make_wrapper(a, sizeof a / sizeof *a))
std::cout << i << ", ";**

Demo.

With C++1Z we will hopefully be able to use std::array_view instead.

Looping over values in dynamically allocated arrays in c++

If you use a std::vector instead of a C-style array, then you can iterate through it with a range-based for loop, like so:

#include <vector>
#include <iostream>

int main() {
int i;
std::cout << "How many values?";
std::cin >> i;

std::vector <int> v;

for (int n = 0; n < i; n++) {
std::cout << "Enter number: " << std::endl;
int x;
std::cin >> x;
v.push_back (x);
}

std::cout << "[";
for (auto e : v)
std::cout << e << ", ";
std::cout << "]" << std::endl;
}

How does the range-based for work for plain arrays?

It works for any expression whose type is an array. For example:

int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
n *= 2;
delete [] arraypointer;

For a more detailed explanation, if the type of the expression passed to the right of : is an array type, then the loop iterates from ptr to ptr + size (ptr pointing to the first element of the array, size being the element count of the array).

This is in contrast to user defined types, which work by looking up begin and end as members if you pass a class object or (if there is no members called that way) non-member functions. Those functions will yield the begin and end iterators (pointing to directly after the last element and the begin of the sequence respectively).

This question clears up why that difference exists.

Range based for loop for heap allocated arrays

A raw array only supports the range-based for syntax if the visible declaration includes the number of elements, i.e. int arr[3] in the first case and int* prt in the second case. In the first case this is given (but still you should prefer std::array if possible), but not in the second case you allocate memory on the heap and the information about the size is gone. You can circumvent this, if you simply use std::array rather than the raw array.

Since the pointer was dereferenced, the types should be the same.

Taking a closer look under the hood, the reason for this is, that in the first case you have an array and in the second case you have a pointer, which is NOT even the same type.

This misinterpretation of pointer and array equaliuty keeps to be broadcasted over C++ tutorials, but this is wrong. This might be due to the fact, that when passing an array to a function taking a pointer of that type, the array decays to a pointer, known as array-decay.

Can I make it work with a small change

Yes you can. Use std::array or st::vector which will make it look like that for std::array:

#include <iostream>
#include <array>

int main()
{
std::array<int, 3>* ptr = new std::array<int, 3>;

for(int& value : *ptr )
std::cout << "success" << std::endl;
}

For brevity I did not include the delete for the pointer which you should always do.

However, if you allocate memory on the heap, it is almost always best to use std::vector as deallocation will be taken care for automatically. The program would than read:

#include <iostream>
#include <vecor>

int main()
{
std::vector<int> vec(3);

for(int& value : vec)
std::cout << "success" << std::endl;
}

for loop based on dynamic list python

Your implementation is probably nesting too many loops for the problem it is trying to solve.

This first implementation contains an error. See below for the fix.

Try something along these lines perhaps:

l = [97,122,111,98,111,98,101,103,103,104,97]
out = []
acc = []
for v in l:
if len(acc)==0 or v >= acc[-1]:
acc.append(v)
else:
if len(acc) > 1:
out.append(acc)
acc = [v]

print(out)
>>>[[97, 122], [98, 111], [98, 101, 103, 103, 104]]

That previous code is slow and can drop the last found fragment. I found that error while running random tests on it to try an optimized version. The following code shows the original code with the correction and the optimized version which can be 30% faster.

def original(l):
out = []
acc = []
added = False
for v in l:
if len(acc)==0 or v >= acc[-1]:
acc.append(v)
else:
added = False
acc = [v]

if acc is not None and len(acc)>1 and not added:
added = True
out.append(acc)
return out

def optimized(l):
out = []

acc = None
tmp = None
deb_v = False
for v in l:
prev = acc[-1] if (acc is not None and len(acc)) else tmp
if prev is not None and v >= prev:
if tmp is not None:
acc = []
acc.append(tmp)
out.append(acc)
tmp = None
acc.append(v)
else:
acc = None
tmp = v
return out

# The original test data
l = [97,122,111,98,111,98,101,103,103,104,97]
assert original(l) == optimized(l) == [[97,122],[98,111],[98,101,103,103,104]]

# A list that triggered last-fragment-dropped error
l = [57, 16, 6, 19, 40, 3, 4, 13, 2, 70, 85, 65, 32, 69, 54, 51, 95, 74, 92, 46, 45, 26, 0, 61, 99, 43, 67, 71, 97, 10, 18, 73, 88, 47, 33, 82, 25, 75, 93, 80, 23, 37, 87, 90, 49, 15, 35, 63, 17, 64, 5, 72, 89, 21, 50, 8, 41, 86, 31, 78, 52, 76, 56, 42, 77, 36, 11, 60, 39, 22, 68, 27, 24, 28, 59, 96, 29, 38, 12, 79, 53, 9, 83, 94, 34, 14, 7, 48, 30, 20, 66, 62, 91, 58, 81, 1, 98, 44, 55, 84]
assert original(l) == optimized(l)

# Random testing
import random
l = list(range(100))
random.shuffle(l)
assert original(l) == optimized(l)

# Timing!
import timeit

print(timeit.timeit("original(l)", globals={"l":l, "original": original}))
# 43.95869998800117

print(timeit.timeit("optimized(l)", globals={"l":l, "optimized": optimized}))
# 34.82134292599949

Access pointer in range based for loop

You could write your own custom iterator that returns a T* when dereferenced

class iterator {
T* ptr;
public:
explicit iterator(T* ptr) : ptr(ptr) {}
iterator& operator++() {ptr++; return *this;}
bool operator!=(iterator other) const {return ptr != other.ptr;}
T* operator*() const {return ptr;}
};

And then return this iterator from your begin and end functions.

I think it is slightly surprising behavior for an array wrapper to have though.

Live demo.



Related Topics



Leave a reply



Submit