Есть ли контейнерный адаптер, который бы изменял направление итераторов, чтобы я мог перебирать контейнер в обратном направлении с помощью цикла for на основе диапазона?
С явными итераторами я бы преобразовал это:
for (auto i = c.begin(); i != c.end(); ++i) { ...
в это:
for (auto i = c.rbegin(); i != c.rend(); ++i) { ...
Я хочу преобразовать это:
for (auto& i: c) { ...
к этому:
for (auto& i: std::magic_reverse_adapter(c)) { ...
Есть ли такая вещь, или я должен написать это сам?
c++
c++11
ranged-loops
Алекс Б
источник
источник
begin
поend
или для работы с потоковыми итераторами и т.п. Алгоритмы диапазона были бы хороши, но они действительно просто синтаксический сахар (за исключением возможности ленивой оценки) по сравнению с алгоритмами итератора.template<typename T> class reverse_adapter { public: reverse_adapter(T& c) : c(c) { } typename T::reverse_iterator begin() { return c.rbegin(); } typename T::reverse_iterator end() { return c.rend(); } private: T& c; };
Это может быть улучшено (добавлениеconst
версий и т. д.), но это работает:vector<int> v {1, 2, 3}; reverse_adapter<decltype(v)> ra; for (auto& i : ra) cout << i;
печатает321
template<typename T> reverse_adapter<T> reverse_adapt_container(T &c) {return reverse_adapter<T>(c);}
так что тогда вы можете просто использоватьfor(auto &i: reverse_adapt_container(v)) cout << i;
для итерации.parallel_for
, с еще более сильным условием «мне все равно, какой порядок», если бы оно было включено в стандарт в той или иной форме. Конечно, он также может иметь синтаксический сахар на основе диапазона :-)Ответы:
На самом деле подталкивания имеет такой адаптер:
boost::adaptors::reverse
.источник
На самом деле, в C ++ 14 это можно сделать с помощью всего лишь нескольких строк кода.
По идее это очень похоже на решение @ Paul. Из-за того, что в C ++ 11 чего-то не хватает, это решение немного раздуто (плюс определение в запахах std). Благодаря C ++ 14 мы можем сделать его более читабельным.
Ключевое наблюдение заключается в том, что циклы for, основанные на диапазоне, работают, полагаясь
begin()
иend()
получая итераторы диапазона. Благодаря ADL даже не нужно определять их обычайbegin()
иend()
в пространстве имен std ::.Вот очень простое решение:
Это работает как шарм, например:
печатает как ожидалось
ПРИМЕЧАНИЕ
std::rbegin()
,std::rend()
иstd::make_reverse_iterator()
еще не реализованы в GCC-4.9. Я пишу эти примеры в соответствии со стандартом, но они не будут компилироваться в стабильный g ++. Тем не менее, добавить временные заглушки для этих трех функций очень просто. Вот пример реализации, которая определенно не завершена, но в большинстве случаев работает достаточно хорошо:источник
forward<T>
в своейreverse
реализации.using namespace std
в заголовке, что не очень хорошая идея. Или я что-то упустил?Это должно работать в C ++ 11 без повышения:
источник
std
пространстве имен имеет неопределенное поведение в соответствии с 17.6.4.2.1.make_reverse_iterator
отсутствует вstd
пространстве имен, поэтому оно не будет конфликтовать с его версией для C ++ 14.Это работает для вас:
источник
например:
источник
Если вы можете использовать диапазон v3 , вы можете использовать адаптер обратного диапазона,
ranges::view::reverse
который позволяет просматривать контейнер в обратном направлении.Минимальный рабочий пример:
Смотрите ДЕМО 1 .
Примечание. Согласно Эрику Ниблеру , эта функция будет доступна в C ++ 20 . Это можно использовать с
<experimental/ranges/range>
заголовком. Тогдаfor
утверждение будет выглядеть так:Смотрите ДЕМО 2
источник
ranges::view
пространство имен было переименовано вranges::views
. Итак, пользуйтесьranges::views::reverse
.Если не использовать C ++ 14, то ниже я найду самое простое решение.
Demo .
Это не работает для контейнеров / типов данных (таких как массив), у которых нет
begin/rbegin, end/rend
функций.источник
Вы можете просто использовать,
BOOST_REVERSE_FOREACH
который повторяется в обратном направлении. Например, кодгенерирует следующий вывод:
источник