在C ++ 11基于范围的for循环中查找元素的位置?
假设我有以下代码:
vector<int> list; for(auto& elem:list) { int i = elem; }
我可以在vector中findelem
的位置,而不需要维护一个单独的迭代器吗?
是的,只需要一些按摩;)
诀窍是使用组合:而不是直接在容器上迭代,而是沿着一个索引“压缩”它。
专门的拉链代码:
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上看到它,虽然它缺lessfor-range循环支持,所以它不太漂亮。
编辑:
只记得我应该更频繁地检查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
自动创build一个视图作为引用和iota(0)
元组的范围,简单地创build一个“ 0
”的范围,从0
开始,只是计数到无限(或者,最大的types…)。
jrok是正确的:基于范围的循环不是为此devise的。
然而,在你的情况下,可以使用指针algorithm来计算它,因为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]); }
但是这显然是一个不好的做法,因为它混淆了代码并使其更脆弱(如果有人改变容器types,重载&
运算符或用'auto'replace'auto',它很容易中断。
注意:C ++ 03中的vector和C ++ 11标准中的数组和string保证了连续性。
不,你不能(至less不是没有努力)。 如果你需要一个元素的位置,你不应该使用基于范围的。 请记住,这只是一个最常见情况的便利工具:为每个元素执行一些代码。 在需要元素位置的不常见的情况下,您必须使用不太方便的常规循环。
如果你有一个支持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
参数。
如果你坚持使用基于范围的索引,并且知道索引,维护索引是非常简单的,如下所示。 我不认为有一个更清洁/更简单的基于范围的循环解决scheme。 但真的为什么不使用(;;)的标准? 这可能会使你的意图和代码最清晰。
vector<int> list; int idx = 0; for(auto& elem:list) { int i = elem; //TODO whatever made you want the idx ++idx; }
我从你的意见中读到,你想知道索引的一个原因是知道元素是序列中的第一个还是最后一个。 如果是这样,你可以做
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 }
有一个令人惊讶的简单的方法来做到这一点
vector<int> list; for(auto& elem:list) { int i = (&elem-&*(list.begin())); }
i
将成为你所需要的索引。
这利用了C ++向量总是连续的事实。