TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #818 6.33K
Как итерироваться в обратном порядке?
#новичкам

Кто часто решал задачки на литкоде поймут проблему. Есть вектор и надо проитерироваться по нему с конца. Ну пишем:

std::vector vec { 1, 2, 3, 4, 5, 6 };
for (auto i = vec.size() - 1; i >= 0; --i) {
std::cout << i << ": " << vec[i] << '\n';
}


В чем проблема этого кода?

Бесконечный цикл и ub. auto определяет тип i беззнаковым, который физически не может быть меньше нуля. Происходит переполнение, i становится очень большим и происходит доступ к невалидной памяти.

В большинстве задач можно написать тип int и все будет работать. Но все-таки size() возвращает size_t и будет происходить сужающее преобразование. В реальных проектах нужно избегать этого и сегодня мы посмотрим, как безопасно итерироваться в обратном порядке.

✅ Использовать свободную функцию ssize() из C++20:

std::vector vec { 1, 2, 3, 4, 5, 6 };
for (auto i = std::ssize(vec) - 1; i >= 0; --i) {
std::cout << vec[i] << '\n';
}


Ее можно применить к вектору и она вернет значение типа std::ptrdiff_t. В первом приблежении это знаковый аналог std::size_t, который позволяет вычислять расстояние между двумя указателями, даже для очень больших массивов.

Так как тип знаковый и в большинстве реализаций его размер сопоставим с size_t, то можно не переживать по поводу возможной срезки длины вектора до меньшего типа.

✅ Использовать обратные итераторы:

std::vector vec { 1, 2, 3, 4, 5, 6 };
for (auto it = std::rbegin(vec); it != std::rend(vec); ++it)
std::cout << *it << '\n';


Тут все довольно очевидно и безопасно.

Однако cppcore гайдлайны говорят нам, что нужно предпочитать использовать range-based-for циклы обычным for'ам. Чтож, давайте пойдем в эту сторону.

✅ Написать свой легковесный адаптер для итерирования в обратном порядке:

template <typename T>
class reverse {
private:
T &iterable_;

public:
explicit reverse(T &iterable) : iterable_{iterable} {}
auto begin() const { return std::rbegin(iterable_); }
auto end() const { return std::rend(iterable_); }
};

std::vector vec{1, 2, 3, 4, 5};
for (const auto &elem : reverse(vec))
std::cout << elem << '\n';


Делаем тонкую обертку над любым итерируемым объектом(в рабочем коде нужно всяких концептов навесить, чтобы было прям по-красоте) и элегантно итерируемся по контейнеру.

✅ А ренджи для кого придумали? Они для этой задачи подходят идеально:

for (const auto& elem : vec | std::views::reverse)
std::cout << elem << '\n';

// или без пайпов

for (const auto& elem : std::ranges::reverse_view(vec))
std::cout << elem << '\n';


Рэнджи из C++20 предоставляют кучу удобных адаптеров для работы с контейнерами. В сущности std::views::reverse или std::ranges::reverse_view делает примерно то же самое, что и мы сами написали в третьем пункте.

Можно совсем упороться и применить алгоритмы ренждей:

std::ranges::copy(vec | std::views::reverse, 
std::ostream_iterator<int>( std::cout,"\n" ));

// или c лямбдой

std::ranges::for_each(vec | std::views::reverse,
[](const auto& elem) {
std::cout << elem << '\n';
});


Бывает, что индексы элементов все-таки нужны внутри цикла. Но это решается с помощью std::ranges::iota_view. Оставляем реализацию этого решения для домашних изысканий.

Have a large toolkit. Stay cool.

#cppcore #cpp20 #STL
  • ❤ 37
  • 👍 24
  • 🔥 11
  • 👎 3
  • 👀 2
  • ❤‍🔥 1
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →