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& .