如何最好地控制迭代方向
How best to control iteration direction?
我有一个大型对象的容器,这些对象的复制成本很高。有时我必须正常地对整个容器进行迭代,有时则相反。一旦我确定了迭代方向,我就不需要在飞行途中进行更改,即不需要随机访问。
我希望能做这样的事情:
#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程序,但我认为这对我没有真正的帮助。我只是没有找到最好的方法。谢谢你的帮助。
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;
}
为什么不将算法放入模板函数中?然后用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);
标准库容器同时具有正常迭代器和反向迭代器,这解决了大部分问题。
不幸的是,它们是不同的类型,因此您无法创建一个可以容纳正常迭代器或反向迭代器的变量。
所以我要做的是将你的循环包装在一个单独的函数中,并将其模板化以同时使用:
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更容易阅读。
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;
}
}
即使这是一个老问题,我最近也遇到了同样的问题,并以这种方式解决了它:
把这个放在某个地方:
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
为true,则它将在项目上向后迭代。
您需要的是制作自己的迭代器包装器。我能想到三种方法:
-
一个类似于两种迭代器类型的
union
;它有两个不同类型的迭代器成员,以及一个选择哪一个是活动的标志。 -
一个包含单个双向迭代器和指示是正向操作还是反向操作的标志的迭代器。
-
某种带有虚拟函数的通用迭代器。(效率低下,但写起来可能很简单)
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 迭代时从向量和内存中删除对象
- 如何在c++迭代器类型中包装std::chrono
- 带过滤器的现代迭代c++集合
- 在c++中检查长方体是否尽可能快地重叠(无迭代)
- C++矢量迭代
- 集合上的输出迭代器:assign和increment迭代器
- Boost Spirit,获取迭代器内部语义动作
- 擦除while循环中迭代的元素
- 实现一个在集合上迭代的模板函数
- 对于set上的循环-获取next元素迭代器
- 在向量内的向量上迭代
- 为什么output_editor Concept不需要output_e迭代器标记
- TSP递归解的迭代形式
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 如何最好地控制迭代方向
- 数组的迭代方向
- 迭代方向枚举