簡體   English   中英

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

我真的覺得這是兩個簡單迭代的大量代碼,並且感覺像是阻塞了代碼並干擾了可讀性。

我認為一些選擇是:

  • 使用#defines-但它將變得不可移植(以在代碼的其他部分中使用)。 此外,如今#define被認為是邪惡的;
  • 遍歷vector-> size()-似乎不是最優雅的方式;
  • 使用函數指針調用方法-但在這種情況下,應位於循環內部的代碼將與循環相去甚遠。

那么,最干凈,優雅的方法是什么呢?

在C ++ 11中,將range-base用於循環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();
    }
}

可以使用以下算法重寫內部循環:

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

flatten會返回每個Polygon中每個Point視圖 ,好像它是一個連續的范圍 請注意,這是一件可以在普通的C ++ 03來完成,而我們是從Boost.Range丟失來完成它是一個flatenning范圍 ,這不應該是很難的范圍方面實現join

否則, 基於范圍的for循環auto一起將幫助您減少迭代拋出范圍並忘記復雜類型的樣板。

如果您不能使用C ++ 11,則可以將迭代器類型的定義縮短為類似

typedef std::vector<Polygon*>::const_iterator PolyCit;
for (PolyCit polygon = polygons.begin(); polygon != polygons.end(); polygon++)

沒關系,因為您在頂層有一個指針向量,所以這對您不起作用,但我會保持下去,因為我認為它很酷。


我的模板元編程有點生銹,因此可能有一種更簡單的方法來做到這一點,但是:

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,則可以編寫接受函子的函數(我相信這些函子在C ++ 0x中的std::tr1下):

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM