Example
int main() {
std::vector items{1, 2, 3};
for (auto [i, v] : std::views::enumerate(items)) {
std::cout << i << v; // prints 011223
}
}
Puzzle
- Can you implement a simplified, standard-compliant version of
views::enumerate
?
/* TODO - views::enumerate */
int main() {
using namespace boost::ut;
"enumerate numbers"_test = [i = 0] {
std::vector items{1, 2, 3};
for (auto [index, value] : views::enumerate(items)) {
expect(_i(i) == index and _i(i + 1) == value);
++mut(i);
}
};
"enumerate strings"_test = [i = 0] {
using std::literals::string_literals::operator""s;
std::vector items{"1"s, "2"s, "3"s};
for (auto [index, value] : views::enumerate(items)) {
expect(_i(i) == index and std::to_string(i + 1) == value);
++mut(i);
}
};
}
Solutions
namespace views {
template <typename C>
struct enumerate {
constexpr enumerate(C& c) : container(c) {}
struct iterator {
constexpr iterator(C& c) : c_it{std::begin(c)} {}
constexpr auto operator*() -> std::pair<int, typename C::reference> {
return {index, *c_it};
}
constexpr auto& operator++() { ++c_it; ++index; return *this; }
typename C::iterator c_it{};
int index{};
};
struct sentinel {
constexpr sentinel(C& c) : c_it{std::end(c)} {}
constexpr bool operator==(iterator i) const {
return i.c_it == c_it;
}
typename C::iterator c_it{};
};
constexpr auto begin() { return iterator{container}; }
constexpr auto end() { return sentinel{container}; }
C& container;
};
}
namespace views {
template <typename TIterated, typename count_type = unsigned int>
struct enumerate {
constexpr explicit(true) enumerate(const std::vector<TIterated>& items)
{
std::transform(std::begin(items), std::end(items), std::back_inserter(enumerated_items), [](const auto& item) {
static auto count = std::numeric_limits<count_type>::min();
return std::pair(count++, item);
});
}
auto begin() const { return std::begin(enumerated_items); }
auto end() const { return std::end(enumerated_items); }
std::vector<std::pair<count_type, TIterated>> enumerated_items {};
};
}
namespace views {
template<template<class...> class C, class E>
struct enumerate {
struct iter {
iter(size_t index, C<E> const& items) : index(index), items(items) {}
bool operator!=(iter& other) { return index != other.index; }
void operator++() { index++; }
auto operator*() { return std::make_pair(index, *(items.begin()+index)); }
size_t index;
const C<E>& items;
};
enumerate(C<E> const& items) : items(items) {}
iter begin() const { return iter(0, items); }
iter end() const { return iter(items.size(), items);}
C<E> const& items;
};
}
namespace views {
template<typename V>
class enumerate_view {
V base_;
class iterator {
using Base = V;
using iterator_t = decltype(std::declval<Base>().begin());
using count_type = decltype(std::declval<Base>().size());
using reference_t = typename Base::reference;
iterator_t current_;
count_type pos_;
public:
struct reference {
const count_type index;
reference_t value;
};
constexpr explicit iterator(iterator_t current, count_type pos) : current_(std::move(current)), pos_(pos) {}
constexpr decltype(auto) operator*() const { return reference{pos_, *current_}; }
constexpr iterator& operator++() { ++pos_; ++current_; return *this; }
friend constexpr bool operator==(const iterator& x, const iterator& y) { return x.current_ == y.current_; }
};
public:
constexpr enumerate_view(V base) : base_(base) {}
constexpr auto begin() { return iterator(base_.begin(), 0); }
constexpr auto end() { return iterator(base_.end(), size()); }
constexpr auto size() { return base_.size(); }
};
constexpr auto enumerate(auto values) { return enumerate_view(values); }
} // namespace views