Диапазон для цикла в динамическом массиве?

20

Существует цикл, основанный на диапазоне, с синтаксисом:

for(auto& i : array)

Он работает с постоянными массивами, но не с динамическими, основанными на указателях, например

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

Он дает ошибки и предупреждения о сбое подстановки, например:

  

Ошибка] C: \ Users \ Siegfred \ Documents \ C-Free \ Temp \ Untitled2.cpp: 16: 16: ошибка: нет соответствующей функции для вызова 'begin (int * & amp;)'

Как использовать этот новый синтаксис с динамическими массивами?

    
задан Maurice Rodriguez 09.04.2013 в 16:37
источник
  • , что говорят об ошибках? По крайней мере, одно сообщение –  Default 09.04.2013 в 16:39
  • он называется диапазоном для цикла и SO, и у Google есть множество примеров –  stijn 09.04.2013 в 16:40
  • Второй экземпляр - опечатка. Должно быть для (auto & i: arr) не массива. –  hmjd 09.04.2013 в 16:42
  • Хмм, когда я прочитал «новый цикл C ++ для цикла на динамическом массиве», я подумал: «Какой глупый вопрос: он просто работает!». Затем я увидел вопрос и понял: «Ох, плакат не означал вектор, когда они набрали« динамический массив »...» –  R. Martinho Fernandes 09.04.2013 в 16:44
  • @Default Может быть, «указатель на динамически выделенный массив» (немного громоздкий, но заставил бы ошибочно думать об указателе OP как о более массивном массиве). –  Christian Rau 09.04.2013 в 18:12
Показать остальные комментарии

5 ответов

21

Чтобы использовать диапазон-for-loop , вам необходимо предоставить функции begin() и end() или перегрузить функции non-member begin() и end() . В последнем случае вы можете обернуть свой диапазон в std::pair и перегрузить begin() и end() для тех:

    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; }
    }

Теперь вы можете использовать for-loop следующим образом:

    for (auto&& i : std::make_pair(array, array + size))
        cout << i << endl;

Обратите внимание, что функции begin() и end() , не являющиеся членами, должны быть перегружены в пространстве имен std здесь, потому что pair также находится в пространстве имен std . Если вы не хотите вмешиваться в стандартное пространство имен, вы можете просто создать свой собственный крошечный класс пар и перегрузить begin() и end() в вашем пространстве имен.

Или создайте тонкую оболочку вокруг вашего динамически выделенного массива и предоставите функции begin() и end() :

    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}; }

И ваш сайт вызова выглядит следующим образом:

    for (auto&& i : wrap_array(array, size))
         std::cout << i << std::endl;

Пример

    
ответ дан user2218982 10.04.2013 в 09:42
источник
  • Нет ли встроенной конструкции для этого? Любой, у кого есть динамический массив C-style, должен написать собственную структуру wrapped_array? –  sffc 23.03.2018 в 23:18
  • @sffc Пока нет, но будет в C ++ 20. Я представляю это в моем новом ответе ниже. –  Baum mit Augen 22.05.2018 в 18:19
16

Вы не можете использовать range-for-loop с динамически распределенными массивами, поскольку компилятор не может вывести начало и конец этого массива. Вы должны всегда использовать вместо него контейнеры, например std::vector .

std::vector<int> v(size);
for(const auto& elem: v)
    // do something
    
ответ дан soon 09.04.2013 в 16:40
источник
  • хорошо, это сработало! я никогда не знал, что C ++ будет безопасным для этого типа, я пришел из C, поэтому он привык управлять динамическими и постоянными массивами одинаково. –  Maurice Rodriguez 09.04.2013 в 16:56
  • @MauriceRodriguez Ну, C имеет такое различие между массивами и указателями точно так же. Например, sizeof (array) возвращает совершенно разные вещи в C тоже, в зависимости от того, является ли это int * array = malloc (N * sizeof (int)); или int array [N] ;. Поэтому просто потому, что C облегчил вам неправильное игнорирование этой разницы, а не то, что этой разницы не было. –  Christian Rau 09.04.2013 в 18:16
  • Я не согласен с тем, что вы всегда должны использовать std :: vector над динамически распределенным массивом. =) Есть случаи, когда важно использовать динамическую версию. Кроме того, поскольку мы используем C ++ 11 в любом случае, std :: array - хорошая идея, чтобы рассмотреть, когда вы используете фиксированный размерный массив. –  Jamin Grey 11.08.2013 в 02:41
  • Не могли бы вы привести мне пример, пожалуйста? Кроме того, std :: array хорош, конечно, но вы должны знать его размер во время компиляции. Это может не охватывать случай ОП. В качестве замены для std :: vector я могу предложить std :: dynarray (начиная с C ++ 1y), если вам не нужно увеличивать размер массива. –  soon 11.08.2013 в 05:45
  • @soon dynarray был отклонен из C ++ 14 («1y») и в лучшем случае имеет только экспериментальный статус, поэтому его, вероятно, не следует использовать в производстве. –  underscore_d 26.06.2016 в 08:00
9

Вы не можете выполнять цикл, основанный на диапазоне, на динамически распределяемом массиве, потому что все, что у вас есть, является указателем на первый элемент. Нет информации о его размере, который компилятор может использовать для выполнения цикла. Идиоматическим решением на C ++ было бы заменить динамически выделенный массив на std::vector :

std::vector<int> arr(size);
for(const auto& i : arr)
  std::cout<< i << std::endl;

Диапазон, основанный на цикле, работает для std::array объектов. Вы должны просмотреть экземпляр массива ( arr ), а не тип ( array ):

std::array<int,10> arr;
for(const auto& i : arr)
  std::cout<< i << std::endl;
    
ответ дан juanchopanza 09.04.2013 в 16:48
источник
  • Вы уже можете использовать диапазон для голого автоматически распределенного массива, не нужно добавлять класс оболочки только для этого. int a [] {1, 2, 3}; for (auto it: a) std :: cout << it << ''; Каждый счетчик элементов создает отдельный тип, для которого конец может быть выведен при компиляции цикла. –  underscore_d 26.06.2016 в 08:11
0

C ++ 20 (предположительно) добавит std::span , что позволяет выполнить цикл таким образом:

#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;
}

К сожалению, это пока не поддерживается текущими компиляторами на момент написания.

Конечно, если у вас есть выбор, предпочтительнее использовать std::vector над массивами C-стиля из get-go.     

ответ дан Baum mit Augen 22.05.2018 в 18:12
источник
-2

Смотрите эту страницу Ссылка и найдите главу " member begin () и end () ". Это может быть то, чего вы хотите достичь.

    
ответ дан Zoka 09.04.2013 в 16:49
источник
  • Вы можете напрямую связать эту главу с якорем #beginend, так вот так. –  Default 09.04.2013 в 16:54
  • Вы должны попытаться добавить содержимое того, что вы хотите, чтобы пользователь читал, поскольку ссылки только ответы несколько обескуражены. –  Default 09.04.2013 в 16:58
  • «Это может быть то, чего вы хотите достичь». Нет, не может. Для этого потребуется какой-то способ вывести размер / последний элемент для динамического массива, заданного только экземпляром его общего типа, что невозможно ... и должно быть очевидно из того факта, что такие функции еще не предусмотрены, для этого точная причина. –  underscore_d 26.06.2016 в 08:04