На самом деле, в C ++ 14 это можно сделать с помощью всего лишь нескольких строк кода.
По идее это очень похоже на решение @ Paul.Из-за того, что в C ++ 11 чего-то не хватает, это решение немного раздуто (плюс определение в запахах std).Благодаря C ++ 14 мы можем сделать его намного более читабельным.
Ключевое наблюдение заключается в том, что циклические циклы for работают, полагаясь на begin()
и end()
для получения итераторов диапазона.,Благодаря ADL даже не нужно определять их пользовательские begin()
и end()
в пространстве имен std ::.
Вот очень простое решение:
// -------------------------------------------------------------------
// --- Reversed iterable
template <typename T>
struct reversion_wrapper { T& iterable; };
template <typename T>
auto begin (reversion_wrapper<T> w) { return std::rbegin(w.iterable); }
template <typename T>
auto end (reversion_wrapper<T> w) { return std::rend(w.iterable); }
template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }
Это работает как брелок, например:
template <typename T>
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<int> { 1, 2, 3, 4, }));
// on const lvalue references
const list<int> ints_list { 1, 2, 3, 4, };
for (auto&& el: reverse(ints_list))
cout << el << ',';
cout << '\n';
// on mutable lvalue references
vector<int> 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,
ПРИМЕЧАНИЕ std::rbegin()
, std::rend()
и std::make_reverse_iterator()
еще не реализованы в GCC-4.9.Я пишу эти примеры в соответствии со стандартом, но они не будут компилироваться в стабильный g ++.Тем не менее, добавить временные заглушки для этих трех функций очень просто.Вот пример реализации, определенно не завершен , но работает достаточно хорошо для большинства случаев:
// --------------------------------------------------
template <typename I>
reverse_iterator<I> make_reverse_iterator (I i)
{
return std::reverse_iterator<I> { i };
}
// --------------------------------------------------
template <typename T>
auto rbegin (T& iterable)
{
return make_reverse_iterator(iterable.end());
}
template <typename T>
auto rend (T& iterable)
{
return make_reverse_iterator(iterable.begin());
}
// const container variants
template <typename T>
auto rbegin (const T& iterable)
{
return make_reverse_iterator(iterable.end());
}
template <typename T>
auto rend (const T& iterable)
{
return make_reverse_iterator(iterable.begin());
}
ОБНОВЛЕНИЕ 22 октября 2017
Благодаря estan для указания на это.
Исходная реализация примера ответа использует using namespace std;
, что приведет к тому, что любой файл, включая эту реализацию (который должен быть в заголовочном файле), также импортируетцелое std
пространство имен.
Пересмотрен пример реализации для предложения using std::rbegin, std::rend
вместо.