我有一个大型对象的容器,复制起来很昂贵。我有时必须正常地迭代整个容器,有时反过来。一旦我确定了迭代方向,我就不需要改变飞行中途,即不需要随机访问。
我希望做这样的模式:
#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程序,但我不认为这对我有帮助。我只是没有看到最好的方法来做到这一点。谢谢你的帮助。
答案 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::transform
或std::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
一样;它有两个不同类型的迭代器成员,以及一个选择哪一个是活动的标志。
包含单个双向迭代器的标记,以及指示是向前还是反向操作的标记。
某种具有虚函数的泛型迭代器。 (效率低下,但写作可能很简单)