Enumeration in a Python-like loop in C ++

Possible duplicate:
Find element position in C ++ 11 loop for loop?

I have a vector , and I would like to iterate it and at the same time have access to the indices for each individual element (I need to pass both the element and its index to the function). I reviewed the following two solutions:

 std::vector<int> v = { 10, 20, 30 }; // Solution 1 for (std::vector<int>::size_type idx = 0; idx < v.size(); ++idx) foo(v[idx], idx); // Solution 2 for (auto it = v.begin(); it != v.end(); ++it) foo(*it, it - v.begin()); 

I was wondering if there could be a more compact solution. Something like Python enumerate . This is the closest I used to use a C ++ 11 range loop, but defining an index outside the loop in the private area definitely seems like a worse solution than 1 or 2:

 { int idx = 0; for (auto& elem : v) foo(elem, idx++); } 

Is there any way (possibly using Boost) to simplify the last example so that the index falls into a closed loop?

+21
c ++ for-loop c ++ 11
Jul 04 2018-12-12T00:
source share
3 answers

As @Kos says, this is such a simple thing that I really don’t see the need to simplify it further and will personally stick to the traditional index cycle, except that I would choose std::vector<T>::size_type and just use std::size_t :

 for(std::size_t i = 0; i < v.size(); ++i) foo(v[i], i); 

I'm not too keen on solution 2. It requires (seemingly hidden) random access iterators, which will not allow you to easily change the container, which is one of the strengths of iterators. If you want to use iterators and make it general (and possibly get a performance hit when iterators are not random access), I would recommend using std::distance :

 for(auto it(v.begin()); it != v.end(); ++it) foo(*it, std::distance(it, v.begin()); 
+10
Jul 04 2018-12-12T00:
source share

Here is some ridiculous solution using lazy rating. First create an enumerate_object generator object:

 template<typename Iterable> class enumerate_object { private: Iterable _iter; std::size_t _size; decltype(std::begin(_iter)) _begin; const decltype(std::end(_iter)) _end; public: enumerate_object(Iterable iter): _iter(iter), _size(0), _begin(std::begin(iter)), _end(std::end(iter)) {} const enumerate_object& begin() const { return *this; } const enumerate_object& end() const { return *this; } bool operator!=(const enumerate_object&) const { return _begin != _end; } void operator++() { ++_begin; ++_size; } auto operator*() const -> std::pair<std::size_t, decltype(*_begin)> { return { _size, *_begin }; } }; 

Then create a wrapper function function that will output the template arguments and return the generator:

 template<typename Iterable> auto enumerate(Iterable&& iter) -> enumerate_object<Iterable> { return { std::forward<Iterable>(iter) }; } 

Now you can use your function as follows:

 int main() { std::vector<double> vec = { 1., 2., 3., 4., 5. }; for (auto&& a: enumerate(vec)) { size_t index = std::get<0>(a); double& value = std::get<1>(a); value += index; } } 

The implementation above is a simple toy: it should work both with const links and with const lvalue links, as well as with rvalue links, but it has real value for the latter, given that it copies the whole iterable object several times. This problem can undoubtedly be solved with the help of additional settings.

Since C ++ 17, decomposition declarations even allow you to have cool syntax like Python to specify the index and value directly in the for initializer:

 int main() { std::vector<double> vec = { 1., 2., 3., 4., 5. }; for (auto&& [index, value] a: enumerate(vec)) { value += index; } } 

I do not have a C ++ 17 compatible compiler to test it, but I hope that the auto&& in the decomposition can output index as std::size_t and value as double& .

+12
Jul 04 2018-12-12T00:
source share

One way is to wrap a loop in its own function.

 #include <iostream> #include <vector> #include <string> template<typename T, typename F> void mapWithIndex(std::vector<T> vec, F fun) { for(int i = 0; i < vec.size(); i++) fun(vec[i], i); } int main() { std::vector<std::string> vec = {"hello", "cup", "of", "tea"}; mapWithIndex(vec, [](std::string s, int i){ std::cout << i << " " << s << '\n'; } ); } 
+1
Jul 04 2018-12-12T00:
source share



All Articles