Как работает range-based для простых массивов?
в C++11 вы можете использовать диапазон на основе for, который действует как foreach других языках. Он работает даже с простыми массивами в C:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
как он знает, когда остановиться? Работает ли он только со статическими массивами, которые были объявлены в той же области for используется? Как бы вы использовали это for с динамическими массивами?
5 ответов:
он работает для любого выражения, тип которого является массивом. Например:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}}; for(int &n : *arraypointer) n *= 2; delete [] arraypointer;для более подробного объяснения, если тип выражения передан справа от
:- Это тип массива, затем цикл повторяется изptrдоptr + size(ptrуказывает на первый элемент массива,sizeбудучи количество элементов массива).это в отличие от пользовательских типов, которые работают на
beginиendкак члены, если вы передаете объект класса или (если нет членов, вызываемых таким образом) функции, не являющиеся членами. Эти функции будут давать начальный и конечный итераторы (указывая непосредственно после последнего элемента и начала последовательности соответственно).этот вопрос проясняет, почему эта разница существует.
Я думаю, что самая важная часть этого вопроса заключается в том, как C++ знает размер массива (по крайней мере, я хотел знать это, когда нашел этот вопрос).
C++ знает размер массива, потому что это часть определения массива - это тип переменной. Компилятор должен знать тип.
С C++11
std::extentможно использовать для получения размера массива:int size1{ std::extent< char[5] >::value }; std::cout << "Array size: " << size1 << std::endl;конечно, это не имеет особого смысла, потому что у вас есть чтобы явно указать размер в первой строке, который вы затем получите во второй строке. Но вы также можете использовать
decltypeи тогда становится еще интереснее:char v[] { 'A', 'B', 'C', 'D' }; int size2{ std::extent< decltype(v) >::value }; std::cout << "Array size: " << size2 << std::endl;
согласно последнему рабочему проекту C++ (n3376) оператор ranged for эквивалентен следующему:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } }так что он знает, как остановить таким же образом обычный
forцикл с использованием итераторов делает.Я думаю, что вы можете искать что-то вроде следующего, чтобы обеспечить способ использования приведенного выше синтаксиса с массивами, которые состоят только из указателя и размера (динамические массивы):
template <typename T> class Range { public: Range(T* collection, size_t size) : mCollection(collection), mSize(size) { } T* begin() { return &mCollection[0]; } T* end () { return &mCollection[mSize]; } private: T* mCollection; size_t mSize; };этот шаблон класса может быть использован для создания диапазона, над которым вы можете повторить с помощью нового ranged for синтаксис. Я использую это для запуска всех объектов анимации в сцене, которая импортируется с помощью библиотеки, которая возвращает только указатель на массив и размер в виде отдельных значений.
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) ) { // Do something with each pAnimation instance here }этот синтаксис, на мой взгляд, гораздо яснее, чем то, что вы получите, используя
std::for_eachили простоforпетли.
Он знает, когда остановиться, потому что он знает границы статических массивов.
Я не уверен, что вы подразумеваете под "динамическими массивами", в любом случае, если не перебирать статические массивы, неофициально компилятор ищет имена
beginиendв области класса объекта, который вы перебираете, или ищетbegin(range)иend(range)используя аргумент-зависимый поиск и использует их в качестве итераторов.для получения дополнительной информации, в стандарте C++11 (или общественные проекты его), "6.5.4 диапазона на основе
forзаявление", ПГ.145
как работает range-based для простых массивов?
это читать как "скажите мне, что ranged-for делает (с массивами)?"
я отвечу, предполагая, что-Возьмите следующий пример с использованием вложенных массивов:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (auto &pl : ia)текст:
ia- это массив массивов ("вложенный массив"), содержащий[3]массивы, каждый из которых содержит[4]значения. В приведенном выше примере петли черезiaэто основной "диапазон" ([3]), и поэтому петли[3]раза. Каждый цикл производит один изia' s[3]главные ценности, начиная с первого и заканчивая последним - массив, содержащий[4]значения.
- первый цикл:
plравна{1,2,3,4}- массив- второго контура:
plравна{5,6,7,8}- массив- третий цикл:
plравна{9,10,11,12}- массивпрежде, чем мы объяснить процесс, вот некоторые дружественные напоминания о массивах:
- массивы интерпретируются как указатели на их первое значение-использование массива без итерации возвращает адрес первого значения
plдолжны быть ссылкой, потому что мы не можем копировать массивы- с массивами, когда вы добавляете число к самому объекту массива, он продвигается вперед, что много раз и "указывает" на эквивалентную запись-если
n- номер в вопросе, тогдаia[n]это то же самое, что*(ia+n)(мы разыменовываем адрес, которыйnзаписи вперед), иia+nэто то же самое, что&ia[n](мы получаем адрес этой записи в массиве).вот что происходит:
- на каждом цикле,
plкак ссылка доia[n]Сnвыравнивание текущего количества циклов, начиная с 0. Итак,plиia[0]на первый раунд, на втором этоia[1]и так далее. Он извлекает значение с помощью итерации.- цикл продолжается до тех пор, пока
ia+nменьшеend(ia)....Вот и все.
это просто упрощенный способ написать это:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int n = 0; n != 3; ++n) auto &pl = ia[n];
если Ваш массив не вложенные, тогда этот процесс становится немного проще в том, что ссылка не нужно, потому что повторенное значение не является массивом, а скорее "нормальным" значением:
int ib[3] = {1,2,3}; // short for (auto pl : ib) cout << pl; // long for (int n = 0; n != 3; ++n) cout << ib[n];
дополнительная информация
что делать, если мы не хотим использовать
autoключевое слово при созданииpl? Как бы это выглядело?в следующем примере,
plотносится кarray of four integers. На каждом циклеplприсваивается значениеia[n]:int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int (&pl)[4] : ia)
и... Вот как это работает, с дополнительной информацией чтобы смахнуть всякую путаницу. Это просто "стенография"
forцикл, который автоматически считается для вас, но не имеет способа получить текущий цикл, не делая это вручную.