如何最好地控制迭代方向?

时间:2012-01-26 17:41:43

标签: c++ c++11 iteration

我有一个大型对象的容器,复制起来很昂贵。我有时必须正常地迭代整个容器,有时反过来。一旦我确定了迭代方向,我就不需要改变飞行中途,即不需要随机访问。

我希望做这样的模式:

#include <iostream>
#include <vector>
using namespace std;
int main( int argc, char** )
{
    // pretend this is a vector of expensive objects
    vector<int> foo = {1,2,3,4,5};

    // calculate forward or backward iteration direction
    bool backwards = (argc > 1);

    if( backwards )
        // prepare backward iteration, but don't copy objects
    else
        // prepare forward iteration, but don't copy objects

    for( auto& i : /* either forward or backward */ )
    {
        // my loop body
        cout << i;
    }

    return 0;
}

这是一个C ++ 11程序,但我不认为这对我有帮助。我只是没有看到最好的方法来做到这一点。谢谢你的帮助。

6 个答案:

答案 0 :(得分:6)

C ++标准容器带有这些称为“反向迭代器”的东西。使用std::vector::rbegin()std::vector::rend()来获取迭代器,该迭代器在向量中向后迭代。 C ++ 03可以很容易地做到这一点:

#include <iostream> 
#include <vector>  

// Use const reference to pass expensive-to-copy types
void loop_body(const int& i)
{
    std::cout << i;
}

int main( int argc, char** ) 
{ 
    // pretend this is a vector of expensive objects 
    std::vector<int> foo = {1,2,3,4,5}; 

    // calculate forward or backward iteration direction 
    bool backwards = (argc > 1); 

    if( backwards ) { 
        std::for_each(foo.rbegin(), foo.rend(), &loop_body);
    } else { 
        std::for_each(foo.begin(), foo.end(), &loop_body);
    } 
    return 0; 
} 

你可以在C ++ 11中使用lambdas来实现这个目的:

#include <iostream> 
#include <vector> 

int main( int argc, char** ) 
{ 
    // pretend this is a vector of expensive objects 
    std::vector<int> foo = {1,2,3,4,5}; 

    // calculate forward or backward iteration direction 
    bool backwards = (argc > 1); 

    // Use const reference to pass expensive-to-copy types
    auto loop_body = [](const int& i)
    {
        std::cout << i;
    };

    if( backwards ) { 
        std::for_each(foo.rbegin(), foo.rend(), loop_body);
    } else { 
        std::for_each(foo.begin(), foo.end(), loop_body);
    } 
    return 0; 
}

答案 1 :(得分:6)

为什么不把算法放到模板函数中?然后用begin / end或rbegin / rend来调用它是微不足道的。

template <class Iterator>
void do_stuff(Iterator first, Iterator last)
{
    // Your loop code here.
}

或者您可以将lambda(因为它是C ++ 11)与std::for_each一起使用:

auto loop_body = [&](int &i) { std::cout << i << std::endl; } ;

if (backward)
  std::for_each(v.rbegin(), v.rend(), loop_body);
else
  std::for_each(v.begin(), v.end(), loop_body);

答案 2 :(得分:1)

标准库容器有正常和反向迭代器,这解决了很大一部分问题。

不幸的是,它们是不同的类型,所以你不能创建一个可以包含普通或反向迭代器的变量。

所以我要做的是将循环包装在一个单独的函数中,并将其模板化以适用于:

template <typename It>
void myloop(It first, It last) {
    for(It cur = first; cur != last; ++cur)
    {
        // my loop body
        cout << *cur;
    }
}

然后像这样称呼它:

if( backwards )
    myloop(foo.rbegin(), foo.rend());
else
    myloop(foo.begin(), foo.end());

当然,您可能也可以使用标准库算法之一而不是循环:

if( backwards )
    std::for_each(foo.rbegin(), foo.rend(), [](int item){  cout << item;});
else
    std::for_each(foo.begin(), foo.end(), [](int item){  cout << item;});

(注意我在这里使用for_each是为了简单起见。很可能,std::transformstd::copy可能更适合描述您想要做的事情。

我还使用了lambda表达式而不是myloop函数。你可以做任何一件事,但是lambda要短得多,而且IMO更容易阅读。

答案 3 :(得分:1)

std::vector<int>::iterator begin = foo.begin();
std::vector<int>::iterator last = foo.end();
if (last != begin)
{
    --last;
    int direction = 1;
    if( backwards )
    {
        std::swap(begin, last);
        direction = -1;
    }
    for( auto& i = begin;  ; i += direction)
    {
        // do stuff
        if (i == last)
            break;
    }
}

答案 4 :(得分:1)

即使这是一个老问题,我最近遇到了同样的问题并以这种方式解决了这个问题:

把它放在某个地方:

namespace mani {

    // an extension to std::for_each where you can choose the direction:
    template <class InputIterator, class Function> Function for_each_reverse(bool reverse, const InputIterator& first, const InputIterator& last, Function fn)
    {
        if (reverse) {
            return std::for_each(std::reverse_iterator<InputIterator>(last), std::reverse_iterator<InputIterator>(first), fn);
        }
        return std::for_each(first, last, fn);
    }

}

...然后你就可以使用它:

auto start = ...
auto end = ...
bool reverse = ...
mani::for_each_reverse(reverse, start, end, [&](const MyItem& item) {
    ...
});

如果reverse为false,它将按正常方向迭代项目。如果reverse为真,则会在项目上向后迭代。

答案 5 :(得分:0)

您需要的是制作自己的迭代器包装器。我可以想到三种方法:

  • 就像两个迭代器类型的union一样;它有两个不同类型的迭代器成员,以及一个选择哪一个是活动的标志。

  • 包含单个双向迭代器的标记,以及指示是向前还是反向操作的标记。

  • 某种具有虚函数的泛型迭代器。 (效率低下,但写作可能很简单)