C ++ 11 обратный диапазон для цикла

Есть ли адаптер контейнера, который бы отменил направление iteratorов, чтобы я мог перебирать контейнер в обратном направлении с помощью цикла for-loop?

С явными iteratorами я бы преобразовал это:

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)) { ... 

Есть ли такая вещь, или я должен написать ее сам?

На самом деле Boost имеет такой адаптер: boost::adaptors::reverse .

 #include  #include  #include  int main() { std::list x { 2, 3, 5, 7, 11, 13, 17, 19 }; for (auto i : boost::adaptors::reverse(x)) std::cout << i << '\n'; for (auto i : x) std::cout << i << '\n'; } 

На самом деле, в C ++ 14 это можно сделать с помощью нескольких строк кода.

Это очень похоже на идею решения Павла. Из-за недостатков в C ++ 11 это решение немного излишне раздувается (плюс определение в std-запахах). Благодаря C ++ 14 мы можем сделать его более читаемым.

Главное наблюдение заключается в том, что for-loops на основе диапазона используются, основываясь на begin() и end() , чтобы получить iteratorы диапазона. Благодаря ADL , даже не нужно определять свои пользовательские функции begin() и end() в пространстве имен std ::.

Вот очень простое решение:

 // ------------------------------------------------------------------- // --- Reversed iterable template  struct reversion_wrapper { T& iterable; }; template  auto begin (reversion_wrapper w) { return std::rbegin(w.iterable); } template  auto end (reversion_wrapper w) { return std::rend(w.iterable); } template  reversion_wrapper reverse (T&& iterable) { return { iterable }; } 

Это работает как шарм, например:

 template  void print_iterable (std::ostream& out, const T& iterable) { for (auto&& element: iterable) out << element << ','; out << '\n'; } int main (int, char**) { using namespace std; // on prvalues print_iterable(cout, reverse(initializer_list { 1, 2, 3, 4, })); // on const lvalue references const list ints_list { 1, 2, 3, 4, }; for (auto&& el: reverse(ints_list)) cout << el << ','; cout << '\n'; // on mutable lvalue references vector ints_vec { 0, 0, 0, 0, }; size_t i = 0; for (int& el: reverse(ints_vec)) el += i++; print_iterable(cout, ints_vec); print_iterable(cout, reverse(ints_vec)); return 0; } 

печатает как ожидалось

 4,3,2,1, 4,3,2,1, 3,2,1,0, 0,1,2,3, 

ПРИМЕЧАНИЕ. В GCC-4.9 еще не реализованы std::rbegin() , std::rend() и std::make_reverse_iterator() . Я пишу эти примеры в соответствии со стандартом, но они не будут компилироваться в стабильном g ++. Тем не менее, добавление временных заглушек для этих трех функций очень просто. Вот примерная реализация, определенно не полная, но достаточно хорошо подходит для большинства случаев:

 // -------------------------------------------------- template  reverse_iterator make_reverse_iterator (I i) { return std::reverse_iterator { i }; } // -------------------------------------------------- template  auto rbegin (T& iterable) { return make_reverse_iterator(iterable.end()); } template  auto rend (T& iterable) { return make_reverse_iterator(iterable.begin()); } // const container variants template  auto rbegin (const T& iterable) { return make_reverse_iterator(iterable.end()); } template  auto rend (const T& iterable) { return make_reverse_iterator(iterable.begin()); } 

ОБНОВЛЕНИЕ 22 окт 2017

Спасибо estan за это.

Исходная реализация образца ответа использует using namespace std; , что приведет к тому, что любой файл, включая эту реализацию (который должен находиться в файле заголовка), также импортирует все пространство имен std .

Пересмотренная реализация образца, чтобы предложить using std::rbegin, std::rend .

Это должно работать на C ++ 11 без повышения:

 namespace std { template T begin(std::pair p) { return p.first; } template T end(std::pair p) { return p.second; } } template std::reverse_iterator make_reverse_iterator(Iterator it) { return std::reverse_iterator(it); } template std::pair()))>, std::reverse_iterator()))>> make_reverse_range(Range&& r) { return std::make_pair(make_reverse_iterator(begin(r)), make_reverse_iterator(end(r))); } for(auto x: make_reverse_range(r)) { ... } 

Это работает для вас:

 #include  #include  #include  #include  #include  int main(int argc, char* argv[]){ typedef std::list Nums; typedef Nums::iterator NumIt; typedef boost::range_reverse_iterator::type RevNumIt; typedef boost::iterator_range irange_1; typedef boost::iterator_range irange_2; Nums n = {1, 2, 3, 4, 5, 6, 7, 8}; irange_1 r1 = boost::make_iterator_range( boost::begin(n), boost::end(n) ); irange_2 r2 = boost::make_iterator_range( boost::end(n), boost::begin(n) ); // prints: 1 2 3 4 5 6 7 8 for(auto e : r1) std::cout << e << ' '; std::cout << std::endl; // prints: 8 7 6 5 4 3 2 1 for(auto e : r2) std::cout << e << ' '; std::cout << std::endl; return 0; } 
  template  struct reverse_wrapper { C & c_; reverse_wrapper(C & c) : c_(c) {} typename C::reverse_iterator begin() {return c_.rbegin();} typename C::reverse_iterator end() {return c_.rend(); } }; template  struct reverse_wrapper< C[N] >{ C (&c_)[N]; reverse_wrapper( C(&c)[N] ) : c_(c) {} typename std::reverse_iterator begin() { return std::rbegin(c_); } typename std::reverse_iterator end() { return std::rend(c_); } }; template  reverse_wrapper r_wrap(C & c) { return reverse_wrapper(c); } 

например:

  int main(int argc, const char * argv[]) { std::vector arr{1, 2, 3, 4, 5}; int arr1[] = {1, 2, 3, 4, 5}; for (auto i : r_wrap(arr)) { printf("%d ", i); } printf("\n"); for (auto i : r_wrap(arr1)) { printf("%d ", i); } printf("\n"); return 0; } 

Если не использовать C ++ 14, я нахожу ниже простейшее решение.

 #define METHOD(NAME, ...) auto NAME __VA_ARGS__ -> decltype(m_T.r##NAME) { return m_T.r##NAME; } template struct Reverse { T& m_T; METHOD(begin()); METHOD(end()); METHOD(begin(), const); METHOD(end(), const); }; #undef METHOD template Reverse MakeReverse (T& t) { return Reverse{t}; } 

Демо .
Он не работает для контейнеров / типов данных (например, массива), у которых нет функций begin/rbegin, end/rend .

  • Что такое std :: prom?
  • Что стандартная библиотека гарантирует самопереключение?
  • с использованием шаблона extern (C ++ 11)
  • Запись универсальной функции memoization в C ++ 11
  • C ++ 11 move constructor не вызывается, предпочитаемый конструктор по умолчанию
  • Когда перегрузка проходит по ссылке (l-value и r-value), предпочтительной для передачи по значению?
  • Почему летучий определитель используется через std :: atomic?
  • Разница между C ++ 11 std :: bind и boost :: bind
  • stoi и std :: to_string on mingw 4.7.1
  • Почему C ++ 11 не поддерживает назначенные списки инициализаторов как C99?
  • Имеет ли этот код из раздела «Язык программирования C ++» раздел 36.3.6 четкого поведения?
  • Давайте будем гением компьютера.