I implemented an enumerate() method for C++ containers similar to Python's enumerate to iterate through a range with an index and the actual value.
I have the following questions:
- Does the usage of the
Const
template parameter make sense for this scenario? - Do I need additional iterator methods depending on the type of iterator, e.g. operator+(int) or operator--?
- Is the construction of the std:pair on dereferencing the iterator too much?
- Should the operator!= method also compare the current index?
- Is there something else important missing here?
Thanks!
Usage:
#include <iostream>
#include <string>
#include <vector>
auto main() -> int {
const std::vector<std::string> names{ "Adam", "Ben", "Chris" };
for (const auto&& [i, name] : enumerate(names))
std::cout << i << ") " << name << '\n';
}
Implementation:
#include <cstddef>
#include <iterator>
#include <utility>
template <class Iter, class T, bool Const = false>
class enumerate_iterator {
public:
using pointer = typename std::conditional_t<Const, const T* const, T*>;
using reference = typename std::conditional_t<Const, const T&, T&>;
enumerate_iterator(Iter iterator, std::size_t index);
auto operator!=(const enumerate_iterator& other) -> bool;
void operator++();
auto operator*() const -> std::pair<std::size_t, reference>;
private:
Iter iterator;
std::size_t index;
};
template <class Container, bool Const = false>
struct enumerate_range {
using value_type = typename Container::value_type;
using container_iterator =
typename std::conditional_t<Const, typename Container::const_iterator,
typename Container::iterator>;
using iterator = enumerate_iterator<container_iterator, value_type, Const>;
using reference = typename std::conditional_t<Const, const Container&, Container&>;
explicit enumerate_range(reference container);
enumerate_range() = delete;
auto begin() -> iterator;
auto end() -> iterator;
private:
reference container;
};
template <class Container>
auto enumerate(const Container& container) -> enumerate_range<Container, true> {
return enumerate_range<Container, true>(container);
}
template <class Container>
auto enumerate(Container& container) -> enumerate_range<Container, false> {
return enumerate_range<Container, false>(container);
}
template <class Container, bool Const>
enumerate_range<Container, Const>::enumerate_range(reference container) : container(container) {}
template <class Container, bool Const>
auto enumerate_range<Container, Const>::begin() -> iterator {
return iterator(container.begin(), 0);
}
template <class Container, bool Const>
auto enumerate_range<Container, Const>::end() -> iterator {
return iterator(container.end(), std::distance(container.begin(), container.end()));
}
template <class Iter, class T, bool Const>
enumerate_iterator<Iter, T, Const>::enumerate_iterator(Iter iterator, std::size_t index)
: iterator(iterator), index(index) {}
template <class Iter, class T, bool Const>
auto enumerate_iterator<Iter, T, Const>::operator!=(const enumerate_iterator& other) -> bool {
return iterator != other.iterator;
}
template <class Iter, class T, bool Const>
void enumerate_iterator<Iter, T, Const>::operator++() {
iterator++;
index++;
}
template <class Iter, class T, bool Const>
auto enumerate_iterator<Iter, T, Const>::operator*() const -> std::pair<std::size_t, reference> {
return std::pair<std::size_t, reference>{index, *iterator};
}