2013-07-19 48 views
2

我已經定義指針的雙端隊列是這樣的:我可以從基於循環的範圍內獲取項目的索引嗎?

std::deque<BoardSquare *> mydeque; 

我想使用基於與我deque的循環範圍:

for (BoardSquare * b : mydeque) { 

    // do something involving the index of b 
} 

是否有可能得到一個項目從索引在基於循環的範圍內?

+14

如果您需要索引,也許您不應該使用基於範圍的循環。 – juanchopanza

+0

也許......這是否意味着答案是'不'?或者你的評論意味着我混淆了我的範例嗎? – BeeBand

+0

我想不出一種不可怕的方式來做到這一點。 – juanchopanza

回答

8

我想出了一個解決方案—或更確切地說是一個實驗的解決方案。這裏是你將如何最終使用它:

for(auto item : make_indexable(v)) 
{ 
     //std::get<0>(item) is the index 
     //std::get<1>(item) is the object 
} 

這裏那張最小實現(它只是爲了演示的基本概念):

#include <tuple> 
#include <functional> 

template<typename C> 
struct indexed_container 
{ 
    struct indexed_iterator 
    { 
     typedef typename C::value_type value_type; 
     typedef std::tuple<size_t, std::reference_wrapper<value_type>> tuple_type; 

     typename C::iterator _it; 
     size_t _index; 

     indexed_iterator(typename C::iterator it) : _it(it), _index(0) {} 

     indexed_iterator& operator++() 
     { 
      ++_it; 
      ++_index; 
      return *this; 
     } 
     bool operator == (indexed_iterator const & other) 
     { 
      return _it == other._it; 
     } 
     bool operator != (indexed_iterator const & other) 
     { 
      return _it != other._it; 
     } 
     tuple_type operator*() 
     { 
      return std::make_tuple(_index, std::ref(*_it)); 
     } 
    }; 

    indexed_container(C & c) : _c(c) {} 

    indexed_iterator begin() 
    { 
     return indexed_iterator(_c.begin()); 
    } 
    indexed_iterator end() 
    { 
     return indexed_iterator(_c.end()); 
    } 
private: 
    C & _c; 
}; 

template<typename C> 
auto make_indexable(C & c) -> indexed_container<C> 
{ 
    return indexed_container<C>(c); 
} 

測試代碼:

#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<int> v{1,2,3}; 
    for(auto item : make_indexable(v)) 
    { 
     std::cout << std::get<0>(item) << " => " << std::get<1>(item) << std::endl; 

     std::get<1>(item) *= 10; //modify value! 
    } 
    std::cout << "\nModified\n"; 
    for(auto item : make_indexable(v)) 
    { 
     std::cout << std::get<0>(item) << " => " << std::get<1>(item) << std::endl; 
    } 
} 

輸出:

0 => 1 
1 => 2 
2 => 3 

Modified 
0 => 10 
1 => 20 
2 => 30 

Online demo

注意,該方案並不完美,因爲它不會與臨時變量和常量容器(和常量對象的容器)工作。另外,現在的基本對象將即使你寫auto item,而不是auto & item(事實上,你可以不寫auto &item)返回參考。但我認爲這些問題可以通過更多的努力和謹慎的設計來解決。畢竟,這只是對基本想法的展示。

+5

不錯,它讓我想起從'python'枚舉':) – juanchopanza

9

不,這不是(至少不是以合理的方式)。當你真正需要的指數,那麼你可能不應該使用循環在所有基於範圍的,而是一個良好的老指數型iterator-或環:

// non-idiomatic index-iteration, random access containers only 
for(std::size_t i=0; i<mydeque.size(); ++i) 
    mydeque[i]; 

// awfully ugly additional iteration variable, yet generic and fast 
std::size_t i = 0; 
for(auto iter=mydeque.begin(); iter!=mydeque.end(); ++iter,++i) 
    *iter; 

// idiomatic and generic, yet slow for non-random access containers 
for(auto iter=mydeque.begin(); iter!=mydeque.end(); ++iter) 
{ 
    auto i = std::distance(mydeque.begin(), iter); 
    *iter; 
} 

還有所有那些有自己的優勢,以及關於清晰度,慣用性,簡化性和性能的缺點。

+0

看到我的答案是一個骯髒的方式來獲取範圍for循環索引。 – TemplateRex

3

你需要添加一個額外的變量來跟蹤指數,複製由基於範圍的循環使用(無法訪問)迭代器。你需要確保它的正確初始化,並增加在每次迭代,是特別小心,如果有人增加continue語句循環就不會出問題。

作爲傳統的for循環的迭代變量,使用索引(或迭代器,您可以根據需要計算索引)會更簡單且不易出錯。

+0

看看我的解決方案。我想聽聽你對此的評論。 – Nawaz

1

除了@ChristianRau提供的優秀答案,它顯示了獲取循環索引的首選方法,有一種方法可以從ranged-for循環獲取索引,但前提是您使用std::vector,因爲這是隻有保證內存中元素連續性的容器。

#include <deque> 
#include <vector> 
#include <iostream> 

int main() 
{ 
    auto v = std::vector<int> { 0, 1, 2, 3 }; 
    auto d = std::vector<int*> { &v[0], &v[1], &v[2], &v[3] }; // NOT: std::deque 

    for (auto ptr: d) 
    { 
     // assumes element contiguity, only guaranteed for std::vector!! 
     auto const i = std::distance(&d[0], &ptr); 
     std::cout << *(d[i]) << "\n"; 
    } 
} 

Live output,會崩潰的std::deque

+0

那麼,當通過引用而不是按值來引用元素時,它更容易(但不會骯髒):'for(auto && b:v)auto i = &b - v.data();',而不需要額外的向量。好的,你必須記住要引用,但是在你的版本中,無論如何你都需要迭代一個完全不同的容器(這證明了這個問題是否仍然是基於循環的相同範圍)。仍然有趣的方法。 –

相關問題