7

假设我有一个多边形向量,其中每个多边形都包含一个点向量。我必须在我的代码中多次迭代所有多边形的所有点,我最终不得不一遍又一遍地编写相同的代码:

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();
        }
}

我真的觉得对于两个简单的迭代来说这是很多代码,并且感觉它阻塞了代码并干扰了可读性。

我认为的一些选择是:

  • 使用#defines - 但它会使不可移植(用于代码的其他部分)。此外,#defines 现在被认为是邪恶的。
  • 遍历 vector->size() - 这似乎不是最优雅的方式;
  • 使用函数指针调用方法 - 但在这种情况下,应该在循环内部的代码将远离循环。

那么,最干净、最优雅的方式是什么?

4

7 回答 7

24

在 C++11 中,使用ranged-base for 循环auto关键字:

for(const auto& polygon : polygons) {
    for(const auto& point : polygon->points) {
        point.DoSomething();
    }
}
于 2013-01-11T21:14:27.553 回答
9

如果您不能使用 C++11,boost 有一个FOREACH宏,它会生成大量代码,但会大大简化您的代码:

BOOST_FOREACH(Polygon * polygon, polygons)
{
    BOOST_FOREACH( Point & point, polygon->points )
    {
        point.doSomething();
    }
}
于 2013-01-11T21:18:32.120 回答
5

可以使用如下算法重写内部循环:

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
        );
    }
);

如果我们有某种复合迭代器,我们可以真正表达您的意图,即为每个多边形中的每个一些事情。像Boost.Range这样的范围库将允许您避免将每个容器命名两次,因为您想使用它们的整个范围

我理想的代码版本如下所示:

for_each( flatten( polygons ), &Point::DoSomething );

areflatten将返回每个中的每个视图,就好像它是一个连续的范围一样。请注意,这是可以在普通C++03中完成的事情,而我们在Boost.Range中缺少的只是一个扁平化的range,这在 range 方面应该不难实现。PointPolygonjoin

否则,基于范围的 for 循环auto帮助您减少迭代抛出范围并忘记复杂类型的样板。

于 2013-01-11T21:17:41.427 回答
5

如果你不能使用 C++11,也许 typedef 迭代器类型更短,比如

typedef std::vector<Polygon*>::const_iterator PolyCit;
for (PolyCit polygon = polygons.begin(); polygon != polygons.end(); polygon++)
于 2013-01-11T21:17:48.670 回答
1

没关系,这对你不起作用,因为你在顶层有一个指针向量,但我会坚持下去,因为我认为它很酷。


我的模板元编程有点生疏,所以可能有更简单的方法可以做到这一点,但是:

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(); });
于 2013-01-11T21:28:29.283 回答
1

你需要一个抽象层。与其处理多边形向量,不如编写一个管理该向量的类。然后该类提供迭代器对来迭代这些点。迭代器中的代码知道并封装了这些细节。

于 2013-01-11T21:28:30.030 回答
0

首先,有一个普通的基于整数的循环。比迭代器短一点。

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();
}
于 2013-01-11T21:37:47.160 回答