Is there an equivalent to a range-based "enumerate" loop from python in modern C ++?

Is there an equivalent to range-based enumerate loop from python in C ++? I would suggest something like this.

 enumerateLoop (auto counter, auto el, container) { charges.at(counter) = el[0]; aa.at(counter) = el[1]; } 

Can this be done with templates or macros?

I know that I can just use the old school for the loop and repeat it until it reaches container.size() . But I wonder how this will be solved using templates or macros.

EDIT

I played a little with boost iterators after the hint in the comments. I got another working solution using C ++ 14.

 template <typename... T> auto zip(const T &... containers) -> boost::iterator_range<boost::zip_iterator< decltype(boost::make_tuple(std::begin(containers)...))>> { auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...)); auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(containers)...)); return boost::make_iterator_range(zip_begin, zip_end); } template <typename T> auto enumerate(const T &container) { return zip(boost::counting_range(0, static_cast<int>(container.size())), container); } 

https://gist.github.com/kain88-de/fef962dc1c15437457a8

+5
source share
7 answers

I wrote something for this some time ago.

Essentially, you need to wrap the iterator and give it a couple of semantics.

AFAIK, there is nothing like that built into the language. And I do not think that he also has an incentive. You pretty much have to roll on your own.

 // Wraps a forward-iterator to produce {value, index} pairs, similar to // python enumerate() template <typename Iterator> struct EnumerateIterator { private: Iterator current; Iterator last; size_t index; bool atEnd; public: typedef decltype(*std::declval<Iterator>()) IteratorValue; typedef pair<IteratorValue const&, size_t> value_type; EnumerateIterator() : index(0), atEnd(true) {} EnumerateIterator(Iterator begin, Iterator end) : current(begin), last(end), index(0) { atEnd = current == last; } EnumerateIterator begin() const { return *this; } EnumerateIterator end() const { return EnumerateIterator(); } EnumerateIterator operator++() { if (!atEnd) { ++current; ++index; atEnd = current == last; } return *this; } value_type operator*() const { return {*current, index}; } bool operator==(EnumerateIterator const& rhs) const { return (atEnd && rhs.atEnd) || (!atEnd && !rhs.atEnd && current == rhs.current && last == rhs.last); } bool operator!=(EnumerateIterator const& rhs) const { return !(*this == rhs); } explicit operator bool() const { return !atEnd; } }; template<typename Iterable> EnumerateIterator<decltype(std::declval<Iterable>().begin())> enumerateIterator(Iterable& list) { return EnumerateIterator<decltype(std::declval<Iterable>().begin())>(list.begin(), list.end()); } template<typename ResultContainer, typename Iterable> ResultContainer enumerateConstruct(Iterable&& list) { ResultContainer res; for (auto el : enumerateIterator(list)) res.push_back(move(el)); return res; } 
+3
source

Enumeration of several variables was an idiom with C. The only complication is that you cannot declare both variables in the for-loop initializer.

 int index; for (auto p = container.begin(), index = 0; p != container.end(); ++p, ++index) 

I do not think that it is becoming simpler (or more powerful) than that.

+4
source

There is a preliminary C ++ 11 solution in addition to this: boost.range.indexed . Unfortunately, it does not work with contour-based C ++ 11 ranges, but only in old multi-level loops. However, with C ++ 17, it should become (almost) as simple as in python using structured bindings

Then it should be possible to implement something that works as follows:

 for (auto& [n,x] : enumerate(vec)) x = n; 

So, waiting a bit more;)

+3
source

You can also more elegantly use the automatic ranges available with C ++ 11:

 int i = 0; for (auto& el : container){ charges.at(counter) = el[0]; aa.at(counter) = el[1]; ++i; } 

You still have to read i manually.

+2
source

Here is a macro-based solution that is probably superior to most others in terms of simplicity, compilation time, and quality of code generation:

 #include <iostream> #define fori(i, ...) if(size_t i = -1) for(__VA_ARGS__) if(i++, true) int main() { fori(i, auto const & x : {"hello", "world", "!"}) { std::cout << i << " " << x << std::endl; } } 

Result:

 $ g++ -o enumerate enumerate.cpp -std=c++11 && ./enumerate 0 hello 1 world 2 ! 
0
source

Tobias Widlund wrote a nice enumerated title only in the Python style for MIT (although C ++ 17):

Github

Blog post

Really nice to use:

 std::vector<int> my_vector {1,3,3,7}; for(auto [i, my_element] : en::enumerate(my_vector)) { // do stuff } 
0
source

Boost :: Range supports this with 1.56 .

 #include <boost/range/adaptor/indexed.hpp> #include <boost/assign.hpp> #include <iterator> #include <iostream> #include <vector> int main(int argc, const char* argv[]) { using namespace boost::assign; using namespace boost::adaptors; std::vector<int> input; input += 10,20,30,40,50,60,70,80,90; // for (const auto& element : index(input, 0)) // function version for (const auto& element : input | indexed(0)) { std::cout << "Element = " << element.value() << " Index = " << element.index() << std::endl; } return 0; } 
0
source

Source: https://habr.com/ru/post/1214333/


All Articles