假設我有以下代碼:沒有保持一個獨立的迭代器在C++ 11基於範圍的for循環中查找元素的位置?
vector<int> list;
for(auto& elem:list) {
int i = elem;
}
我能找到的矢量elem
的位置?
假設我有以下代碼:沒有保持一個獨立的迭代器在C++ 11基於範圍的for循環中查找元素的位置?
vector<int> list;
for(auto& elem:list) {
int i = elem;
}
我能找到的矢量elem
的位置?
不,你不能(至少不是沒有努力)。如果您需要元素的位置,則不應使用基於範圍的元素。請記住,它只是最常見情況下的便捷工具:爲每個元素執行一些代碼。在需要元素位置的不常見情況下,您必須使用不太方便的常規for
循環。
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標準。
是的,它是在標準規定。 C++ 03中的'vector'和C++ 11中的'array'和'string'保證了連續性。 –
「*如果有人......會輕易破壞......重載'&'運算符*」這就是std :: addressof'的用途。 : - ] – ildjarn
你說得對。因此,&overload證明版本是:int pos = addressof(elem) - addressof(list [0]); .... Matthieu M.的迭代器包裝是更好的:) –
是的,你可以,它只是需要一些按摩;)
的訣竅是用的組合物:在容器直接代替迭代,你「壓縮」,它與沿途的索引。
專業拉鍊碼:
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
」開始的「虛假」範圍,並且僅計入無窮大(或者,它的類型的最大值......)。
這提供了大量有用的信息。謝謝。我會玩弄代碼。正如我上面提到的,「索引」代碼是我希望提供的語言。 –
'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
@ildjarn:是的,Boost.Iterators具有構建塊(看起來),但是沒有相應的範圍,這很煩人。 –
,我從你的意見是一個原因,你想知道索引讀取的是知道的元素是第一個/最後的順序排列。如果是這樣,你可以做
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
}
如果你有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
參數。
如果堅持使用基於對範圍,要知道指數,它是非常容易的,如下圖所示,以保持索引。 我不認爲有一個更清潔/更簡單的基於範圍循環的解決方案。但真的爲什麼不使用(;;)的標準?這可能會使你的意圖和代碼最清晰。
vector<int> list;
int idx = 0;
for(auto& elem:list) {
int i = elem;
//TODO whatever made you want the idx
++idx;
}
(idx相當於「維護一個單獨的迭代器」) – user66081
有一個非常簡單的方法來做到這
vector<int> list;
for(auto& elem:list) {
int i = (&elem-&*(list.begin()));
}
其中i
將是你所需要的索引。
這需要的事實,即C++ vectors are always contiguous。
那不是基於範圍的是(嘿,是一個雙關語?) – jrok
這是不可能的STL容器,除非使用'的std :: find'或其他一些矯枉過正的功能。你不能從包含的元素中得出迭代器。爲什麼不維護迭代器? –
有兩個原因。首先是我想要做的(在這種情況下)是看我是否在最後一個元素:)第二個是編譯器必須保持一個,爲什麼我不能訪問它? 「this」是一個由編譯器維護的變量,爲什麼不在這裏?或者提供一個替代(但仍然很方便)的語法,就像javascript所做的那樣,它會設置一個隨着你循環而變化的變量。對於(汽車及指標:列表) –