Вопрос:

Range-based for loop on a dynamic array?

c++ arrays c++11 foreach dynamic-arrays

16755 просмотра

6 ответа

348 Репутация автора

There is a range-based for loop with the syntax:

for(auto& i : array)

It works with constant arrays but not with pointer based dynamic ones, like

int *array = new int[size];
for(auto& i : array)
   cout<< i << endl;

It gives errors and warnings about failure of substitution, for instance:

Error] C:\Users\Siegfred\Documents\C-Free\Temp\Untitled2.cpp:16:16: error: no matching function for call to 'begin(int*&)'

How do I use this new syntax with dynamic arrays?

Автор: Maurice Rodriguez Источник Размещён: 09.04.2013 02:37

Ответы (6)


16 плюса

22589 Репутация автора

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
Автор: awesoon Размещён: 09.04.2013 02:40

10 плюса

195531 Репутация автора

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.

Автор: juanchopanza Размещён: 09.04.2013 02:48

-2 плюса

1579 Репутация автора

See this page http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer and find the chapter "non-member begin() and end()". This could be what you want to achieve.

Автор: Zoka Размещён: 09.04.2013 02:49

24 плюса

0 Репутация автора

Решение

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

Автор: user2218982 Размещён: 10.04.2013 07:42

1 плюс

42693 Репутация автора

C++20 will (presumably) add 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;
}

Unfortunately, this does not yet appear to be supported by current compilers as of the time of writing.

Of course, if you have the choice, it is preferable to use std::vector over C-style arrays from the get-go.

Автор: Baum mit Augen Размещён: 22.05.2018 04:12

0 плюса

2370 Репутация автора

Instead of defining std::begin and std::end for std::pair of pointers (defining them in std::, by the way, is undefined behaviour) and rolling out your own wrapper, as suggested before, you can use boost::make_iterator_range:

size_t size = 16;
int *dynamic_array = new int[size];
for (const auto& i : boost::make_iterator_range(dynamic_array, dynamic_array + size))
    std::cout << i << std::endl;

Live example.

Автор: Dev Null Размещён: 25.07.2018 12:31
Вопросы из категории :
32x32