Какой лучший способ сделать обратный цикл в C / C# / C++?

Мне нужно вернуться назад по массиву, поэтому у меня есть такой код:

for (int i = myArray.Length - 1; i >= 0; i--)
{
    // Do something
    myArray[i] = 42;
}

Есть ли лучший способ сделать это?

Обновление: я надеялся, что, возможно, в C# есть встроенный механизм для этого, например:

foreachbackwards (int i in myArray)
{
    // so easy
}

Обновление 2: Есть способы получше являются. Руна забирает приз с:

for (int i = myArray.Length; i-- > 0; )
{    
    //do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
    // do something
}

что выглядит даже лучше на обычном C (спасибо Twotymz):

for (int i = lengthOfArray; i--; )
{    
    //do something
}

По большей части этот вопрос представляет собой своего рода обзор ответов ниже. Предлагаем значительно сократить его.

einpoklum 06.11.2013 15:12

Я не уверен, что понимаю, почему какая-либо из альтернатив лучше, если совершенство включает ясность или ремонтопригодность.

dkretz 09.11.2008 20:44

Правда. Я надеялся найти более разумный способ сделать это, поскольку мне приходится делать это довольно часто.

MusiGenesis 09.11.2008 21:04

Удалите C и C++ из заголовка и хэштегов.

jaskmar 31.07.2020 09:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
105
4
155 385
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

Это определенно лучший способ для любого массива, длина которого представляет собой целочисленный тип со знаком. Для массивов, длина которых представляет собой целочисленный тип без знака (например, std::vector в C++), вам необходимо немного изменить конечное условие:

for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
    // blah

Если вы только что сказали i >= 0, это всегда верно для целого числа без знака, поэтому цикл будет бесконечным.

В C++ "i--" автоматически переходит к максимальному значению, если я был равен 0? Извините, у меня проблемы с C++.

MusiGenesis 09.11.2008 18:19

Удивительный. Вы, ребята, только что помогли мне понять причину ошибки заполнения жесткого диска в том, что я написал 12 лет назад. Хорошо, что я не работаю на C++.

MusiGenesis 09.11.2008 18:34

условие завершения должно быть: i <myArray.size (). Зависит только от свойств переполнения unsigned int; а не детали реализации целых чисел и приведений. Следовательно, легче читать и работать на языках с альтернативными целочисленными представлениями.

ejgottl 09.11.2008 18:39

Я думаю, что для меня лучшее решение - остаться в мире C#, где я не могу никому навредить.

MusiGenesis 09.11.2008 18:45

Мне нравится. Если индексатор был беззнаковым (uint и т. д.), Возможно, вам придется это принять во внимание. Назовите меня ленивым, но в этом (беззнаковом) случае я мог бы просто использовать переменную-счетчик:

uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
    arr[--pos] = 42;
}

(на самом деле, даже здесь вам нужно быть осторожным с такими случаями, как arr.Length = uint.MaxValue ... может быть! = где-то ... конечно, это очень маловероятный случай!)

Однако с "--pos" сложно. В прошлом у меня были коллеги, которые любили случайным образом «исправлять» в коде вещи, которые им не казались подходящими.

MusiGenesis 09.11.2008 18:48

Затем используйте .arr.Length-1 и pos--

Marc Gravell 09.11.2008 18:55

Я попытаюсь ответить на свой вопрос здесь, но мне это тоже не очень нравится:

for (int i = 0; i < myArray.Length; i++)
{
    int iBackwards = myArray.Length - 1 - i; // ugh
    myArray[iBackwards] = 666;
}

Вместо того, чтобы каждый раз делать .Length - 1 - i, возможно, рассмотреть вторую переменную? См. Мой [обновленный] пост.

Marc Gravell 09.11.2008 18:21

Проголосовал против моего собственного вопроса. Жесткий. Он не хуже оригинала.

MusiGenesis 09.11.2008 20:58

В C# с использованием Linq:

foreach(var item in myArray.Reverse())
{
    // do something
}

Это, безусловно, самый простой вариант, но обратите внимание, что в текущей версии CLR реверсирование списка всегда является операцией O (n), а не операцией O (1), как это могло бы быть, если это IList <T>; см. connect.microsoft.com/VisualStudio/feedback/…

Greg Beech 10.11.2008 04:20

Похоже, .Reverse () создает локальную копию массива, а затем выполняет итерацию в обратном направлении. Кажется неэффективным копировать массив только для того, чтобы вы могли его перебирать. Я полагаю, что любой пост с "Linq" достоин голосов. :)

MusiGenesis 10.11.2008 06:07

Возможен компромисс, но тогда вы сталкиваетесь с проблемой, что «проголосовало за ответ» (i -> 0) может привести к более медленному коду, потому что (i) необходимо проверять на размер массива каждый раз, когда он используется как в index.

Keltex 10.11.2008 23:16

Хотя я согласен с тем, что Linq великолепен, проблема с этим подходом заключается в том, что итератор не позволяет вам выборочно выбирать элементы массива - рассмотрите ситуацию, когда вы хотите перемещаться по части массива, но не по всему массиву. предмет...

jesses.co.tt 18.02.2016 00:55

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

Использование std :: vector

Использование итераторов

C++ позволяет делать это с помощью std::reverse_iterator:.

for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
    /* std::cout << *it; ... */
}

Использование индексов

Целочисленный тип без знака, возвращаемый std::vector<T>::size, - это нет всегда std::size_t. Это может быть больше или меньше. Это очень важно для работы цикла.

for(std::vector<int>::size_type i = someVector.size() - 1; 
    i != (std::vector<int>::size_type) -1; i--) {
    /* std::cout << someVector[i]; ... */
}

Это работает, поскольку значения целочисленных типов без знака определяются по модулю их количества битов. Таким образом, если вы устанавливаете -N, вы попадаете на (2 ^ BIT_SIZE) -N.

Использование массивов

Использование итераторов

Мы используем std::reverse_iterator для выполнения итерации.

for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a); 
    it != itb; 
    ++it) {
    /* std::cout << *it; .... */
}

Использование индексов

Мы можем безопасно использовать здесь std::size_t, в отличие от приведенного выше, поскольку sizeof всегда возвращает std::size_t по определению.

for(std::size_t i = (sizeof a / sizeof *a) - 1; i != (std::size_t) -1; i--) {
   /* std::cout << a[i]; ... */
}

Как избежать ошибок с применением sizeof к указателям

На самом деле описанный выше способ определения размера массива - отстой. Если a на самом деле является указателем, а не массивом (что случается довольно часто, и новички могут его запутать), он молча завершится ошибкой. Лучше использовать следующее, которое не сработает во время компиляции, если задан указатель:

template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];

Он работает, сначала получая размер переданного массива, а затем объявляя возвращение ссылки на массив типа char того же размера. char определяется как sizeof из: 1. Таким образом, возвращаемый массив будет иметь sizeof из: N * 1, что мы и ищем, только с оценкой времени компиляции и нулевыми накладными расходами во время выполнения.

Вместо того, чтобы делать

(sizeof a / sizeof *a)

Измените свой код так, чтобы он

(sizeof array_size(a))

Кроме того, если в вашем контейнере нет обратного итератора, вы можете использовать реализацию reverse_iterator в boost: boost.org/doc/libs/1_36_0/libs/iterator/doc/…

MP24 09.11.2008 20:24

ваш array_size, похоже, работает для статически распределенных массивов, но не работал для меня, например, когда a был 'new int [7]'.

Nate Parsons 29.12.2008 08:12

да, это и есть цель :) new int [7] возвращает указатель. поэтому sizeof (new int [7]) возвращает не 7 * sizeof (int), а возвращает sizeof (int *). array_size в этом случае вызывает ошибку компиляции, вместо того, чтобы работать в автоматическом режиме.

Johannes Schaub - litb 29.12.2008 08:22

также попробуйте array_size (+ statically_allocated_array), что также не удастся, потому что оператор + превращает массив в указатель на его первый элемент. использование простого sizeof снова даст вам размер указателя

Johannes Schaub - litb 29.12.2008 08:23

Для начала - почему бы просто не использовать end и begin в обратном порядке?

Tomáš Zato - Reinstate Monica 06.01.2016 18:56
for (auto i = someVector.size(); i--;) попроще.
L. F. 10.01.2020 15:34
Ответ принят как подходящий

Хотя это, по общему признанию, немного неясно, но я бы сказал, что наиболее приятный с точки зрения типографии способ сделать это -

for (int i = myArray.Length; i --> 0; )
{
    //do something
}

Когда я впервые прочитал ваш ответ, казалось, что он даже не компилируется, поэтому я решил, что вы сумасшедший. Но это именно то, что я искал: лучший способ написать цикл for в обратном направлении.

MusiGenesis 10.11.2008 06:59

Я думаю, что i -> 0; это специально. Вот что он имеет в виду под "типографически приятным".

Johannes Schaub - litb 10.11.2008 07:08

Я сам нашел это "типографически запутанным". Для меня знак «-» выглядит неправильно, если он не находится рядом с переменной, на которую он влияет.

MusiGenesis 10.11.2008 07:11

Я изменил его версию обратно. Таким образом, это выглядит лучше эстетически, но также похоже, что это даже не валидный код.

MusiGenesis 10.11.2008 07:19

Это довольно круто - не уверен, будет ли это хорошей идеей использовать, но все же довольно круто

hugoware 13.11.2008 17:16

Я предпочитаю это: for (int i = myArray.Length; i-->0 ;) - думаю, это принесет одно дополнительное очко WTF.

Hamish Grubijan 10.08.2010 05:47

@Hamish, к сожалению, это вызовет segfault, когда вы попытаетесь получить доступ к массиву [i], и он попадет в ячейку памяти сразу после конца массива. Если бы не segfault, в худшем случае вы бы никогда не попали в первый элемент массива, так как 0 > 0 == false.

corsiKa 26.06.2011 04:29

@corsiKa: он никогда не получит доступ к myArray [i].

Marcelo Cantos 14.03.2012 04:51

Это мило, но мило ≠ в хорошем стиле. Все, что замедляет чтение, - плохая идея. Кроме того, это больше, чем просто for (int i = myArray.Length; i--;) { … }.

Marcelo Cantos 14.03.2012 04:54

Это слишком непонятно и запутанно. Я бы никогда не написал что-то подобное в производственном коде ...

Mihai Todor 22.06.2012 17:04

Это так вводит в заблуждение. Думал, это другой оператор. Это не. Но круто.

Meredith 18.11.2013 05:52

Ааа, переходит к оператору (->) делает свое дело!

nawfal 10.01.2014 16:59
for (int i = myArray.Length; i-- > 0; ) Вот, починил.
Thomas Eding 23.04.2015 19:16

Это не непонятно, это единственное правильное действие (после изменения int на size_t).

lalala 06.11.2019 22:56

В то время как i приближается к 0, то есть: i --> 0; функционально такой же, как (i = i - 1) + 1 > 0. Какая красивая @Rune.

Lapys 05.01.2020 06:52

Разве myArray.Length не должен быть «myArray.Length -1», поскольку индексы массива начинаются с нуля?

PastExpiry.com 14.01.2020 18:16

@ PastExpiry.com нет, потому что первый шаг сравнения (и, следовательно, уменьшения) выполняется до первого выполнения тела цикла.

Ruslan 10.09.2020 09:57

ПРИМЕЧАНИЕ: этот пост оказался более подробным и, следовательно, не по теме, прошу прощения.

При этом мои сверстники читают его и считают, что это «где-то» ценно. Эта ветка не к месту. Буду признателен за ваш отзыв о том, куда это следует направить (я новичок на сайте).


В любом случае это версия C# в .NET 3.5, которая удивительна тем, что работает с любым типом коллекции, используя определенную семантику. Это мера по умолчанию (повторное использование!), А не минимизация производительности или цикла ЦП в наиболее распространенном сценарии разработки, хотя, похоже, никогда не бывает того, что происходит в реальном мире (преждевременная оптимизация).

*** Метод расширения, работающий с любым типом коллекции и принимающий делегат действия, ожидающий одно значение типа, все выполняется для каждого элемента в обратном порядке **

Требования 3.5:

public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed)
      {
          foreach (var contextItem in sequenceToReverse.Reverse())
              doForEachReversed(contextItem);
      }

Более старые версии .NET или вы хотите лучше разобраться во внутреннем устройстве Linq? Читайте дальше .. Или нет ..

ПРЕДПОЛОЖЕНИЕ. В системе типов .NET тип Array наследуется от интерфейса IEnumerable (а не от универсального IEnumerable, только от IEnumerable).

Это все, что вам нужно повторить от начала до конца, однако вы хотите двигаться в противоположном направлении. Поскольку IEnumerable работает с массивом типа 'object', допустим любой тип,

Критическая мера: мы предполагаем, что если вы можете обрабатывать любую последовательность в обратном порядке, что «лучше», то это можно делать только с целыми числами.

Решение a для .NET CLR 2.0-3.0:

Описание: мы примем любой реализующий экземпляр IEnumerable с мандатом, согласно которому все содержащиеся в нем экземпляры относятся к одному типу. Итак, если мы получаем массив, весь массив содержит экземпляры типа X. Если какие-либо другие экземпляры имеют тип! = X, генерируется исключение:

Одноэлементный сервис:

открытый класс ReverserService { private ReverserService () {}

    /// <summary>
    /// Most importantly uses yield command for efficiency
    /// </summary>
    /// <param name = "enumerableInstance"></param>
    /// <returns></returns>
    public static IEnumerable ToReveresed(IEnumerable enumerableInstance)
    {
        if (enumerableInstance == null)
        {
            throw new ArgumentNullException("enumerableInstance");
        }

        // First we need to move forwarad and create a temp
        // copy of a type that allows us to move backwards
        // We can use ArrayList for this as the concrete
        // type

        IList reversedEnumerable = new ArrayList();
        IEnumerator tempEnumerator = enumerableInstance.GetEnumerator();

        while (tempEnumerator.MoveNext())
        {
            reversedEnumerable.Add(tempEnumerator.Current);
        }

        // Now we do the standard reverse over this using yield to return
        // the result
        // NOTE: This is an immutable result by design. That is 
        // a design goal for this simple question as well as most other set related 
        // requirements, which is why Linq results are immutable for example
        // In fact this is foundational code to understand Linq

        for (var i = reversedEnumerable.Count - 1; i >= 0; i--)
        {
            yield return reversedEnumerable[i];
        }
    }
}



public static class ExtensionMethods
{

      public static IEnumerable ToReveresed(this IEnumerable enumerableInstance)
      {
          return ReverserService.ToReveresed(enumerableInstance);
      }
 }

[TestFixture] открытый класс Testing123 {

    /// <summary>
    /// .NET 1.1 CLR
    /// </summary>
    [Test]
    public void Tester_fornet_1_dot_1()
    {
        const int initialSize = 1000;

        // Create the baseline data
        int[] myArray = new int[initialSize];

        for (var i = 0; i < initialSize; i++)
        {
            myArray[i] = i + 1;
        }

        IEnumerable _revered = ReverserService.ToReveresed(myArray);

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));
    }

    [Test]
    public void tester_why_this_is_good()
    {

        ArrayList names = new ArrayList();
        names.Add("Jim");
        names.Add("Bob");
        names.Add("Eric");
        names.Add("Sam");

        IEnumerable _revered = ReverserService.ToReveresed(names);

        Assert.IsTrue(TestAndGetResult(_revered).Equals("Sam"));


    }

    [Test]
    public void tester_extension_method()
  {

        // Extension Methods No Linq (Linq does this for you as I will show)
        var enumerableOfInt = Enumerable.Range(1, 1000);

        // Use Extension Method - which simply wraps older clr code
        IEnumerable _revered = enumerableOfInt.ToReveresed();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }


    [Test]
    public void tester_linq_3_dot_5_clr()
    {

        // Extension Methods No Linq (Linq does this for you as I will show)
        IEnumerable enumerableOfInt = Enumerable.Range(1, 1000);

        // Reverse is Linq (which is are extension methods off IEnumerable<T>
        // Note you must case IEnumerable (non generic) using OfType or Cast
        IEnumerable _revered = enumerableOfInt.Cast<int>().Reverse();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }



    [Test]
    public void tester_final_and_recommended_colution()
    {

        var enumerableOfInt = Enumerable.Range(1, 1000);
        enumerableOfInt.PerformOverReversed(i => Debug.WriteLine(i));

    }



    private static object TestAndGetResult(IEnumerable enumerableIn)
    {
      //  IEnumerable x = ReverserService.ToReveresed(names);

        Assert.IsTrue(enumerableIn != null);
        IEnumerator _test = enumerableIn.GetEnumerator();

        // Move to first
        Assert.IsTrue(_test.MoveNext());
        return _test.Current;
    }
}

Чувак или чувак: я просто искал менее неуклюжий способ написать «for (int i = myArray.Length - 1; i> = 0; i--)».

MusiGenesis 09.11.2008 21:01

в этом ответе нет никакой ценности

Matt Melton 11.08.2014 18:45

Я бы использовал код в исходном вопросе, но если вы действительно хотите использовать foreach и иметь целочисленный индекс в C#:

foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
    myArray[i] = 42; 
}

В C мне нравится делать это:


int i = myArray.Length;
while (i--) {
  myArray[i] = 42;
}

Пример C#, добавленный MusiGenesis:

{int i = myArray.Length; while (i-- > 0)
{
    myArray[i] = 42;
}}

Мне это нравится. Мне потребовалась секунда или две, чтобы понять, что ваш метод работает. Надеюсь, вы не возражаете против добавленной версии C# (дополнительные фигурные скобки предназначены для того, чтобы индекс имел ту же область видимости, что и в цикле for).

MusiGenesis 10.11.2008 06:37

Я не уверен, что смогу использовать это в производственном коде, потому что это вызовет универсальный "WTF is this?" реакция со стороны тех, кто должен был его поддерживать, но на самом деле это легче набрать, чем обычный цикл for, и без знаков минус или символов> =.

MusiGenesis 10.11.2008 06:42

Жаль, что версии C# нужно дополнительное "> 0".

MusiGenesis 10.11.2008 06:44

Я не знаю, на каком это языке. но ваш первый фрагмент кода, безусловно, НЕ С :), просто чтобы сказать вам очевидное. может быть, вы хотели написать C#, а html не смог его разобрать или что-то в этом роде?

Johannes Schaub - litb 13.11.2008 18:17

@litb: я думаю, что "myArray.Length" недействителен C (?), но часть цикла while работает и отлично выглядит.

MusiGenesis 14.11.2008 06:30

Ой. Ваша правильная инициализация i - это не C. Я просто пытался использовать исходный фрагмент OP, чтобы проиллюстрировать точку.

Twotymz 15.11.2008 18:16

Выглядит здорово, но, согласен, на первый взгляд сбивает с толку.

Martín Coll 31.07.2012 17:35

В C#, используя Visual Studio 2005 или новее, введите "forr" и нажмите [TAB] [TAB]. Это расширится до цикла for, который проходит через коллекцию в обратном направлении.

Так легко ошибиться (по крайней мере, для меня), что я подумал, что вставить этот фрагмент будет хорошей идеей.

Тем не менее, мне нравятся Array.Reverse() / Enumerable.Reverse(), а затем лучше повторять вперед - они более четко формулируют намерение.

Лучший способ сделать это в C++ - это, вероятно, использовать адаптеры итератора (или, лучше, диапазона), которые будут лениво преобразовывать последовательность по мере ее прохождения.

В основном,

vector<value_type> range;
foreach(value_type v, range | reversed)
    cout << v;

Отображает диапазон "range" (здесь он пуст, но я уверен, что вы можете добавлять элементы самостоятельно) в обратном порядке. Конечно, простая итерация диапазона не очень полезна, но передача этого нового диапазона в алгоритмы и прочее - это довольно круто.

Этот механизм также можно использовать для гораздо более мощных целей:

range | transformed(f) | filtered(p) | reversed

Будет лениво вычислять диапазон «range», где функция «f» применяется ко всем элементам, элементы, для которых «p» не соответствует действительности, удаляются, и, наконец, результирующий диапазон меняется на противоположный.

Синтаксис канала - самый читаемый IMO, учитывая его инфикс. Ожидаемое рассмотрение обновления библиотеки Boost.Range реализует это, но это довольно просто сделать и самостоятельно. Еще круче с лямбда-DSEL генерировать функцию f и предикат p в строке.

Вы можете сказать нам, откуда у вас "foreach"? Это #define для BOOST_FOREACH? Выглядит мило во всех отношениях.

Johannes Schaub - litb 10.11.2008 07:06
// this is how I always do it
for (i = n; --i >= 0;){
   ...
}

Я бы предпочел всегда чистый код вместо кода типографически приятный. Таким образом, я бы всегда использовал:

for (int i = myArray.Length - 1; i >= 0; i--)  
{  
    // Do something ...  
}    

Вы можете рассматривать это как стандартный способ обратного цикла. Всего два цента ...

Работал. Еще не делал этого на C#, но спасибо.

PCPGMR 15.10.2014 19:22

Так как же быть, если массив действительно большой (поэтому индекс должен быть беззнакового типа)? size_t на самом деле беззнаковый, не так ли?

lalala 06.11.2019 22:54

Вы можете объявить i как uint (см. Сообщение Марка Гравелла).

Jack Griffin 10.11.2019 19:57

Не работает для беззнакового шрифта из-за переноса текста, в отличие от типографически приятного шрифта. Нехорошо.

Ocelot 22.07.2020 15:23

@lalala Вы можете использовать long long. Он имеет как минимум 64 бита, поэтому в вашем массиве может быть до 2 ^ 63 записей. Этого должно хватить на время ...

Elmar Zander 13.01.2021 19:12

Я предпочитаю цикл while. Для меня это более понятно, чем уменьшение i в состоянии цикла for

int i = arrayLength;
while(i)
{
    i--;
    //do something with array[i]
}

Для C++:

Как упоминалось другими, когда это возможно (т.е. когда вам нужен только каждый элемент за раз), настоятельно рекомендуется использовать итераторы, чтобы они были явными и избегали распространенных ошибок. Современный C++ имеет более сжатый синтаксис для auto:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
    std::cout<<*it<<" ";
}

печатает 4 3 2 1 .

Вы также можете изменить значение во время цикла:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
    *it = *it + 10;
    std::cout<<*it<<" ";
}

что привело к тому, что 14 13 12 11 был напечатан, а {11, 12, 13, 14} впоследствии оказался в std::vector.

Если вы не планируете изменять значение во время цикла, вы должны убедиться, что вы получаете сообщение об ошибке при попытке сделать это случайно, аналогично тому, как можно было бы написать for(const auto& element : vec). Это возможно так:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.crbegin(); it != vec.crend(); ++it) { // used crbegin()/crend() here...
    *it = *it + 10; // ... so that this is a compile-time error
    std::cout<<*it<<" ";
}

Ошибка компилятора в этом случае для меня:

/tmp/main.cpp:20:9: error: assignment of read-only location ‘it.std::reverse_iterator<__gnu_cxx::__normal_iterator<const int*, std::vector<int> > >::operator*()’
   20 |     *it = *it + 10;
      |     ~~~~^~~~~~~~~~

Также обратите внимание, что вы не должны использовать вместе разные типы итераторов:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.end(); ++it) { // mixed rbegin() and end()
    std::cout<<*it<<" ";
}

приводит к многословной ошибке:

/tmp/main.cpp: In function ‘int main()’:
/tmp/main.cpp:19:33: error: no match for ‘operator!=’ (operand types are ‘std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >’ and ‘std::vector<int>::iterator’ {aka ‘__gnu_cxx::__normal_iterator<int*, std::vector<int> >’})
   19 | for (auto it = vec.rbegin(); it != vec.end(); ++it) {
      |                              ~~ ^~ ~~~~~~~~~
      |                              |            |
      |                              |            std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}
      |                              std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >

Если у вас в стеке есть массивы в стиле C, вы можете делать такие вещи:

int vec[] = {1,2,3,4};
for (auto it = std::crbegin(vec); it != std::crend(vec); ++it) {
    std::cout<<*it<<" ";
}

Если вам действительно нужен индекс, рассмотрите следующие варианты:

  • проверьте диапазон, затем работайте со знаковыми значениями, например:
void loop_reverse(std::vector<int>& vec) {
    if (vec.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
        throw std::invalid_argument("Input too large");
    }
    const int sz = static_cast<int>(vec.size());
    for(int i=sz-1; i >= 0; --i) {
        // do something with i
    }
}
  • Работайте с беззнаковыми значениями, будьте осторожны и добавляйте комментарии, например:
void loop_reverse2(std::vector<int>& vec) {
    for(size_t i=vec.size(); i-- > 0;) { // reverse indices from N-1 to 0
        // do something with i
    }
}
  • рассчитать фактический индекс отдельно, например:
void loop_reverse3(std::vector<int>& vec) {
    for(size_t offset=0; offset < vec.size(); ++offset) {
        const size_t i = vec.size()-1-offset; // reverse indices from N-1 to 0
        // do something with i
    }
}

Другие вопросы по теме