2014-01-19 91 views
8

有沒有辦法在C++ 11基於範圍的for循環中訪問迭代器(假設沒有循環索引 ..?)?基於'索引'跳過基於範圍的?

通常我們需要對容器的第一個元素進行一些特殊的操作,並遍歷剩餘的元素。
我正在尋找類似的c++11_get_index_of聲明在這個僞代碼:

for (auto& elem: container) 
{ 
    if (c++11_get_index_of(elem) == 0) 
    continue; 

    // do something with remaining elements 
} 

我真的想避免回到old-style manual iterator handling代碼在那種情況下..

+2

使用能夠提供所需功能的構造。也就是說,使用普通的'for'循環(如果這可以很容易地解決你的問題)。不要強迫自己做複雜的事情,不需要太複雜。 – Nawaz

+1

使用具有首選範圍的STL算法。如果算法中沒有任何一個適合,只能回退到for循環。 –

+0

@Nawaz正確,但如何使用if(elem == container.front())繼續;'作爲解決方法?不是太複雜,是嗎?我們仍然可以使用簡潔的語法。仍然比在pre-C++ 11中手動迭代器處理的3行更好恕我直言...... – Jay

回答

16

我們經常需要做一些特別有 容器的第一個元素和遍歷剩餘的元素。

我很驚訝地看到,沒有人提出這個解決方案迄今:

auto it = std::begin(container); 

    // do your special stuff here with the first element 

    ++it; 

    for (auto end=std::end(container); it!=end; ++it) { 

     // Note that there is no branch inside the loop! 

     // iterate over the rest of the container 
    } 

它具有很大的優勢,該分支被移出循環。它使循環更加簡單,編譯器也可以更好地優化它。

如果你堅持在for循環基於範圍的,也許最簡單的做到這一點的方法是這樣的(還有其他的,醜陋的方式):

std::size_t index = 0; 

for (auto& elem : container) { 

    // skip the first element 
    if (index++ == 0) { 
    continue; 
    } 

    // iterate over the rest of the container 
} 

但是,我會認真移動分支出來的循環如果你只需要跳過第一個元素。

+2

絕對喜歡在循環外分支 – Jay

+0

優雅的解決方案 - 哇,別人知道preincrement存在! +1 –

2

沒有,您無法在基於範圍的for循環中獲取迭代器(當然,無需查看容器中的元素)。迭代器由標準定義爲__begin,但這僅用於說明。如果您需要迭代器,則打算使用正常的for循環。基於範圍的for循環存在的原因是您不需要關心自己處理迭代的情況。

隨着autostd::beginstd::end,你for循環仍然應該是很簡單的:

for (auto it = std::begin(container); it != std::end(container); it++) 
+0

這裏使用後增加會引入無意義的開銷潛力,特別是對於迭代器是類類型而不是指針的容器。我希望每個人都可以擺脫違約postinc/dec的習慣。 –

2

如何使用一個簡單的for循環與iteratos:

for(auto it = container.begin(); it != container.end(); it++) 
{ 
    if(it == container.begin()) 
    { 
     //do stuff for first 
    } 
    else 
    { 
     //do default stuff 
    } 
} 

它不是基於範圍,但它的功能。 如果你仍然可能要使用的範圍內循環:

int counter = 0; 
for(auto &data: container) 
{ 
    if(counter == 0) 
    { 
     //do stuff for first 
    } 
    else 
    { 
     //do default stuff 
    } 
    counter++; 
} 
+0

+1迭代器是要走的路,而不是指數。 – Mehrdad

+0

[那些應該是前增量](https://stackoverflow.com/questions/21215947/skipping-in-range-based-for-based-on-index#comment77339338_21216043) –

0

有沒有知道的元素多遠是在容器內,而不必迭代器,指針或侵入性指標的方式。這裏做一個簡單的方法:

int index= 0; 
for (auto& elem: container) 
{ 
    if (index++ == something) 
    continue; 

    // do something with remaining elements 
} 

如果你想跳過第一個元素,另一種方法是使用一個std ::雙端隊列和pop_front的第一個元素。然後你可以像往常一樣使用容器來執行循環。

2

在遍歷元素時,始終優先使用algorithm,並且僅在沒有任何算法適合的情況下使用簡單的for循環。

選擇正確的算法取決於你想要做什麼與你沒有告訴我們的元素。

如果你想跳過第一個元素,自卸例如:

if (!container.empty()) { 
    for_each(++container.begin(), container.end(), [](int val) { cout << val; }); 
} 
+0

我明白你的觀點,雖然它通常是一個我們用我們的業務對象做的一堆事情,所以即使lambda可能會變得非常大而且失控。當我們需要跳過處理其中一個元素時,仍然存在'for_each'的問題,不是嗎? – Jay

+0

如果跳過是基於索引,你可能做錯了。重組您的數據。 –

0

當我需要在隨機存取容器上做這樣的事情時,我的習慣是迭代索引。

for(std::size_t i : indexes(container)) { 
    if (i==0) continue; 
    auto&& e = container[i]; 
    // code 
} 

唯一棘手的部分是寫作indexes,它返回什麼升壓稱counting迭代器的範圍中。從迭代器創建基本的可迭代範圍很簡單:使用boost的範圍概念,或者自己編寫。

的基本範圍的任意的迭代器類型是:

template<typename Iterator> 
struct Range { 
    Iterator b; Iterator e; 
    Range(Iterator b_, Iterator e_):b(b_), e(e_) {}; 
    Iterator begin() const { return b; } 
    Iterator end() const { return e; } 
}; 

它可以gussy了一堆,但這是核心。

2

加速提供了一個很好簡潔的方式做到這一點:

std::vector<int> xs{ 1, 2, 3, 4, 5 }; 
for (const auto &x : boost::make_iterator_range(xs.begin() + 1, xs.end())) { 
    std::cout << x << " "; 
} 
// Prints: 2 3 4 5 

您可以找到boost/range/iterator_range.hppmake_iterator_range