在c++中迭代的优雅方式
Elegant way to iterate in C++
假设我有一个多边形向量,其中每个多边形包含一个点向量。我必须在代码中多次遍历所有多边形的所有点,我最终不得不一遍又一遍地编写相同的代码:
for(std::vector<Polygon*>::const_iterator polygon = polygons.begin();
polygon != polygons.end(); polygon++)
{
for(std::vector<Point>::const_iterator point = (*polygon)->points.begin();
point != (*polygon)->points.end(); point++)
{
(*point).DoSomething();
}
}
我真的觉得对于两个简单的迭代来说,这是很多代码,并且感觉它阻塞了代码并干扰了可读性。
我认为有以下几个选项:
- 使用#define -但它会使不可移植(在代码的其他部分使用)。此外,#define现在被认为是邪恶的;
- 迭代vector->size() -这似乎不是最优雅的方式;
- 使用函数指针调用方法——但是在这种情况下,应该在循环内部的代码将远离循环。
在c++ 11中,使用range -base for循环和auto
关键字:
for(const auto& polygon : polygons) {
for(const auto& point : polygon->points) {
point.DoSomething();
}
}
如果你不能使用c++ 11, boost有一个FOREACH宏,它会生成很多代码,但会极大地简化你的代码:
BOOST_FOREACH(Polygon * polygon, polygons)
{
BOOST_FOREACH( Point & point, polygon->points )
{
point.doSomething();
}
}
如果不能使用c++ 11,可以将迭代器类型定义为较短的类型,如
typedef std::vector<Polygon*>::const_iterator PolyCit;
for (PolyCit polygon = polygons.begin(); polygon != polygons.end(); polygon++)
内部循环可以使用如下算法重写:
std::for_each(
(*polygon)->points.begin(), (*polygon)->points.end(),
&Point::DoSomething
);
与外部循环混合使用稍微复杂一点:
std::for_each(
polygons.begin(), polygons.end(),
[]( Polygon* polygon ) {
std::for_each(
polygon->points.begin(), polygon->points.end(),
&Point::DoSomething
);
}
);
如果我们有某种复合迭代器,我们可以真正表达你的意图,即为每个多边形中的每个点做一些。还有一个range库,比如Boost。Range允许您避免对每个容器命名两次,因为您希望使用它们的整个范围。
我理想的代码版本应该是这样的:
for_each( flatten( polygons ), &Point::DoSomething );
是flatten
将返回每个Polygon
中每个Point
的视图,就好像它是一个连续的范围。请注意,这是可以在普通的 c++ 03中完成的,而我们在Boost中所缺少的。范围完成它是一个扁平化范围,这应该不难实现范围join
。
否则,基于范围的for循环与auto
一起将帮助您减少迭代抛出范围并忘记复杂类型的样板。
您需要一个抽象层。与其处理多边形向量,不如编写一个管理该向量的类。然后,该类提供迭代器对,用于遍历这些点。迭代器中的代码知道并封装了这些细节。
没关系,这对你不起作用,因为你在顶层有一个指针向量,但我会保持它,因为我觉得它很酷。
我的模板元编程有点生疏,所以可能有更简单的方法来做到这一点,但是:
template<typename C, typename F, size_t depth>
struct nested_for_each_helper
{
static void do_it(C& c, F& f)
{
for (auto& i : c)
nested_for_each_helper<decltype(i),F,depth-1>::do_it(i,f);
}
};
template<typename C, typename F>
struct nested_for_each_helper<C,F,0>
{
static void do_it(C& c, F& f)
{
f(c);
}
};
template<size_t depth, typename C, typename F>
void nested_for_each(C& c, F& f)
{
nested_for_each_helper<C,F,depth>::do_it(c,f);
}
int main()
{
int n[3][3][3][3];
int i = 0;
nested_for_each<4>(n,[&i](int& n) { n = i++; });
nested_for_each<4>(n,[](int n){
std::cout << n << ' ';
});
}
根据你的情况,你可以这样使用它(不,你不能):
nested_for_each<2>(polygons, [](Point const& p) { p.DoSomething(); });
首先,有一个普通的老式基于整数的循环。比迭代器短一点。
for( int i = 0 ; i < polygons.size() ; i++ )
{
for( int j = 0 ; j < polygons[i]->points.size(); j++)
{
Point* p = polygons[i]->points[j] ;
p->DoSomething();
}
}
如果你不喜欢这样,并且你没有c++ 11可用,你可以编写接受函子的函数(我相信这些是在std::tr1
下的c++ 0x中):
void eachPoint( function<void (Point* p)> func )
{
for( int i = 0 ; i < polygons.size() ; i++ )
{
for( int j = 0 ; j < polygons[i]->points.size(); j++)
{
Point* p = polygons[i]->points[j] ;
func(p);
}
}
}
或者,一个普通的旧宏:
#define EACH_POLYGON( polyCollection ) for( int _polyI = 0 ; _polyI < polyCollection.size() ; _polyI++ )
for( int _ptNo = 0, Point* p=polyCollection[_polyI]->points[0] ; j < polyCollection[_polyI]->points.size() && (p=polyCollection[_polyI]->points[_ptNo]); _ptNo++)
EACH_POLYGON( polyCollection )
{
p->DoSomething();
}
- 如何以优化的方式同时迭代两个间距不相等的数组
- 以迭代方式合并标准::unordered_map
- C++以迭代方式搜索混合类型地图
- 需要循环帮助以迭代方式添加到程序集中的总和变量
- 迭代文本对的最佳方式
- 为什么以这种方式使用迭代器有效?
- 如何以链式方式在两个容器上构建迭代器
- 通过提升属性树(递归方式)发出 YAML 迭代
- 实现迭代器通用方法的正确方式
- 以迭代方式编写递归代码
- 以迭代方式编写递归函数
- 在C++中以迭代方式连接两个字符串
- std::算法友好的方式迭代'std::vector<std::vector<something>>
- 我可以以这种方式返回迭代器吗?
- 如何以优雅高效的方式实现各种迭代器类别
- 使用提升预处理器以迭代方式调用可变参数模板
- 对链表进行迭代的不同方式
- 以排序方式迭代 std::vector<std::p air<T,U> > 对象
- 如何编写一个可以以类型安全的方式迭代泛型类型的函数
- 在c++wxDir中以非递归方式迭代目录