2012-06-09 72 views
59

假設我有以下代碼:沒有保持一個獨立的迭代器在C++ 11基於範圍的for循環中查找元素的位置?

vector<int> list; 
for(auto& elem:list) { 
    int i = elem; 
} 

我能找到的矢量elem的位置?

+15

那不是基於範圍的是(嘿,是一個雙關語?) – jrok

+2

這是不可能的STL容器,除非使用'的std :: find'或其他一些矯枉過正的功能。你不能從包含的元素中得出迭代器。爲什麼不維護迭代器? –

+2

有兩個原因。首先是我想要做的(在這種情況下)是看我是否在最後一個元素:)第二個是編譯器必須保持一個,爲什麼我不能訪問它? 「this」是一個由編譯器維護的變量,爲什麼不在這裏?或者提供一個替代(但仍然很方便)的語法,就像javascript所做的那樣,它會設置一個隨着你循環而變化的變量。對於(汽車及指標:列表) –

回答

15

不,你不能(至少不是沒有努力)。如果您需要元素的位置,則不應使用基於範圍的元素。請記住,它只是最常見情況下的便捷工具:爲每個元素執行一些代碼。在需要元素位置的不常見情況下,您必須使用不太方便的常規for循環。

23

jrok是正確的:基於範圍的循環不是爲此設計的。

然而,在你的情況下,可以計算它使用指針運算因爲vector存儲其元素連續(*)

vector<int> list; 
for(auto& elem:list) { 
    int i = elem; 
    int pos = &elem-&list[0]; // pos contains the position in the vector 

    // also a &-operator overload proof alternative (thanks to ildjarn) : 
    // int pos = addressof(elem)-addressof(list[0]); 

} 

但這顯然是一個不好的做法,因爲它混淆了代碼&使得它更脆弱(很容易,如果有人改變了容器類型劃分,超載&操作員或「自動」好運取代「汽車&」來調試!)

注:毗連在C++ 03保證矢量,以及C中的數組和字符串++ 11標準。

+6

是的,它是在標準規定。 C++ 03中的'vector'和C++ 11中的'array'和'string'保證了連續性。 –

+1

「*如果有人......會輕易破壞......重載'&'運算符*」這就是std :: addressof'的用途。 : - ] – ildjarn

+0

你說得對。因此,&overload證明版本是:int pos = addressof(elem) - addressof(list [0]); .... Matthieu M.的迭代器包裝是更好的:) –

53

是的,你可以,它只是需要一些按摩;)

的訣竅是用的組合物:在容器直接代替迭代,你「壓縮」,它與沿途的索引。

專業拉鍊碼:

template <typename T> 
struct iterator_extractor { typedef typename T::iterator type; }; 

template <typename T> 
struct iterator_extractor<T const> { typedef typename T::const_iterator type; }; 


template <typename T> 
class Indexer { 
public: 
    class iterator { 
     typedef typename iterator_extractor<T>::type inner_iterator; 

     typedef typename std::iterator_traits<inner_iterator>::reference inner_reference; 
    public: 
     typedef std::pair<size_t, inner_reference> reference; 

     iterator(inner_iterator it): _pos(0), _it(it) {} 

     reference operator*() const { return reference(_pos, *_it); } 

     iterator& operator++() { ++_pos; ++_it; return *this; } 
     iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } 

     bool operator==(iterator const& it) const { return _it == it._it; } 
     bool operator!=(iterator const& it) const { return !(*this == it); } 

    private: 
     size_t _pos; 
     inner_iterator _it; 
    }; 

    Indexer(T& t): _container(t) {} 

    iterator begin() const { return iterator(_container.begin()); } 
    iterator end() const { return iterator(_container.end()); } 

private: 
    T& _container; 
}; // class Indexer 

template <typename T> 
Indexer<T> index(T& t) { return Indexer<T>(t); } 

,並用它:

#include <iostream> 
#include <iterator> 
#include <limits> 
#include <vector> 

// Zipper code here 

int main() { 
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9}; 

    for (auto p: index(v)) { 
     std::cout << p.first << ": " << p.second << "\n"; 
    } 
} 

您可以在ideone看到它,但它缺乏的範圍循環支持,所以它不太漂亮。

編輯:

只記得,我應該更經常檢查Boost.Range。不幸的是沒有zip範圍,但我確實發現了perl:boost::adaptors::indexed。但是它需要訪問迭代器才能獲取索引。殤:X

counting_range和通用 zip我相信它可能是可能做一些有趣的事情

否則......

在理想的世界裏,我會想象:

int main() { 
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9}; 

    for (auto tuple: zip(iota(0), v)) { 
     std::cout << tuple.at<0>() << ": " << tuple.at<1>() << "\n"; 
    } 
} 

隨着zip自動創建一個視圖作爲引用的一系列元素,並簡單地創建一個從「0」開始的「虛假」範圍,並且僅計入無窮大(或者,它的類型的最大值......)。

+0

這提供了大量有用的信息。謝謝。我會玩弄代碼。正如我上面提到的,「索引」代碼是我希望提供的語言。 –

+2

'counting_range'(或['boost :: counting_iterator'](http://www.boost.org/libs/iterator/doc/counting_iterator)html))+ ['boost :: zip_iterator'](http://www.boost.org/libs/iterator/doc/zip_iterator.html)? – ildjarn

+0

@ildjarn:是的,Boost.Iterators具有構建塊(看起來),但是沒有相應的範圍,這很煩人。 –

2

,我從你的意見是一個原因,你想知道索引讀取的是知道的元素是第一個/最後的順序排列。如果是這樣,你可以做

for(auto& elem:list) { 
// loop code ... 
    if(&elem == &*std::begin(list)){ ... special code for first element ... } 
    if(&elem == &*std::prev(std::end(list))){ ... special code for last element ... } 
// if(&elem == &*std::rbegin(list)){... (C++14 only) special code for last element ...} 
// loop code ... 
} 

編輯:例如,該打印容器跳過最後一個元素的分隔符。適用於大多數容器我能想象(包括數組),(在線演示http://coliru.stacked-crooked.com/a/9bdce059abd87f91):

#include <iostream> 
#include <vector> 
#include <list> 
#include <set> 
using namespace std; 

template<class Container> 
void print(Container const& c){ 
    for(auto& x:c){ 
    std::cout << x; 
    if(&x != &*std::prev(std::end(c))) std::cout << ", "; // special code for last element 
    } 
    std::cout << std::endl; 
} 

int main() { 
    std::vector<double> v{1.,2.,3.}; 
    print(v); // prints 1,2,3 
    std::list<double> l{1.,2.,3.}; 
    print(l); // prints 1,2,3 
    std::initializer_list<double> i{1.,2.,3.}; 
    print(i); // prints 1,2,3 
    std::set<double> s{1.,2.,3.}; 
    print(s); // print 1,2,3 
    double a[3] = {1.,2.,3.}; // works for C-arrays as well 
    print(a); // print 1,2,3 
} 
+0

請注意(在不正確的downvoting之前)問題的作者在檢測容器的for-ranged循環中的最後一個元素的上下文中詢問這個問題。爲此,我看不出爲什麼比較'&elem'和'&* std :: prev(std :: end(list))''不起作用或不實際。我同意另一個答案,即基於迭代器的for更適合於此,但仍然如此。 – alfC

+0

在循環和測試if(-i == 0)之前,聲明'int i = c.size();似乎更容易。 –

+0

@MarcGlisse,'int i'代碼只是一個例子。我會刪除它以避免混淆。即使你在循環之前使用'size',你也需要一個計數器。 – alfC

10

如果你有C++ 14的支持編譯器,你可以在一個實用的風格做到這一點:

#include <iostream> 
#include <string> 
#include <vector> 
#include <functional> 

template<typename T> 
void for_enum(T& container, std::function<void(int, typename T::value_type&)> op) 
{ 
    int idx = 0; 
    for(auto& value : container) 
     op(idx++, value); 
} 

int main() 
{ 
    std::vector<std::string> sv {"hi", "there"}; 
    for_enum(sv, [](auto i, auto v) { 
     std::cout << i << " " << v << std::endl; 
    }); 
} 

與叮噹3.4和gcc 4.9(不與4.8);因爲兩者都需要設置-std=c++1y。你需要C++ 14的原因是lambda函數中的auto參數。

3

如果堅持使用基於對範圍,要知道指數,它是非常容易的,如下圖所示,以保持索引。 我不認爲有一個更清潔/更簡單的基於範圍循環的解決方案。但真的爲什麼不使用(;;)的標準?這可能會使你的意圖和代碼最清晰。

vector<int> list; 
int idx = 0; 
for(auto& elem:list) { 
    int i = elem; 
    //TODO whatever made you want the idx 
    ++idx; 
} 
+0

(idx相當於「維護一個單獨的迭代器」) – user66081

1

有一個非常簡單的方法來做到這

vector<int> list; 
for(auto& elem:list) { 
    int i = (&elem-&*(list.begin())); 
} 

其中i將是你所需要的索引。

這需要的事實,即C++ vectors are always contiguous