Элегантный способ итерации условно вперед или назад

Я должен обработать std::vector либо вперед, либо назад, в зависимости от логического флага. Какой самый элегантный способ сделать это? Прежде чем нужно было сделать это в обратном порядке, у меня было:

BOOST_FOREACH(const CType &foo, vec) {
    ...
}

Однако теперь у меня ужасный вид:

for (int i=undoing ? (vec.size()-1) : 0; undoing ? (i >= 0) : (i < vec.size()); undoing ? (i--) : (i++)) {
    const CType &foo = vec[i];
    ...
}

Есть ли способ лучше?


person Claudiu    schedule 15.04.2014    source источник
comment
Поддержка С++11? Я предполагаю, что нет.   -  person Yakk - Adam Nevraumont    schedule 16.04.2014
comment
@Yakk: Нет, хотя мне все равно было бы интересно увидеть этот ответ.   -  person Claudiu    schedule 16.04.2014
comment
минимальное упрощение вашего кода для (unsigned i=undoing ? (vec.size()-1) : 0; i ‹ vec.size(); undoing ? (i--) : (i++))   -  person Alexander    schedule 16.04.2014
comment
См. раздел stackoverflow.com/questions/22360697/   -  person 101010    schedule 16.04.2014


Ответы (4)


Я не знаю, назовут ли его элегантным, но есть:

auto do_it = [](const CType& elem)
             {
                 ...
             };
if (iterate_forward) {
    std::for_each(vec.begin(), vec.end(), do_it);
}
else {
    std::for_each(vec.rbegin(), vec.rend(), do_it);
}
person Max Lybbert    schedule 15.04.2014
comment
О, интересно. Я предпочитаю это ответу с наибольшим количеством голосов, так как мне не нужно повторно реализовывать часть цикла самостоятельно. - person Claudiu; 16.04.2014
comment
Да, согласен, лучше. По возможности следует использовать алгоритмы STL. Конечно, лямбда-выражение может быть заменено унарной функцией для работы с компилятором C++98. - person Adam Wulkiewicz; 16.04.2014

Добавьте функцию шаблона, которая работает либо с прямыми итераторами, либо с обратными итераторами. Вызовите функцию, используя соответствующий итератор на основе значения undoing.

template <typename Iterator>
void doStuff(Iterator iter, Iterator end)
{
   for ( ; iter != end; ++iter )
   {
      // Do stuff
   }
}

if ( undoing )
{
   doStuff(vec.rbegin(), vec.rend());
}
else
{
   doStuff(vec.begin(), vec.end());
}
person R Sahu    schedule 15.04.2014

Как насчет того, чтобы цикл работал от 0 до vector.size, но считывал массив в нужном вам направлении.

int idx;
for (int i =0; i < vec.size(); i ++)
{
   if (undoing) // assuming going forward
     idx = i;
   else // going backwards
     idx = vec.size() - i - 1;

  const CType &foo = vec[idx];
}
person user3494754    schedule 15.04.2014

Вы также можете использовать решение на основе Boost.Range. Он похож на тот, который использует уже предложенные алгоритмы STL.

#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/algorithm/for_each.hpp>

// In C++11 lambda expression can be used instead
struct my_fun
{
    void operator()(const CType& elem) const
    {
        /*...*/
    }
};

/*...*/

using namespace boost::adaptors;

if ( iterate_forward )
    boost::for_each(my_vect, my_fun());
else
    boost::for_each(my_vect | reversed, my_fun());
person Adam Wulkiewicz    schedule 15.04.2014