#pragma once #include #include #include namespace NPrivate { template struct TEnumerator { private: using TStorage = TAutoEmbedOrPtrPolicy; using TValue = std::tuple()))>; using TIteratorState = decltype(std::begin(std::declval())); using TSentinelState = decltype(std::end(std::declval())); static constexpr bool TrivialSentinel = std::is_same_v; struct TIterator; struct TSentinelCandidate { TSentinelState Iterator_; }; using TSentinel = std::conditional_t; struct TIterator { using difference_type = std::ptrdiff_t; using value_type = TValue; using pointer = TValue*; using reference = TValue&; using iterator_category = std::input_iterator_tag; TValue operator*() { return {Index_, *Iterator_}; } TValue operator*() const { return {Index_, *Iterator_}; } void operator++() { ++Index_; ++Iterator_; } bool operator!=(const TSentinel& other) const { return Iterator_ != other.Iterator_; } bool operator==(const TSentinel& other) const { return Iterator_ == other.Iterator_; } std::size_t Index_; TIteratorState Iterator_; }; public: using iterator = TIterator; using const_iterator = TIterator; using value_type = typename TIterator::value_type; using reference = typename TIterator::reference; using const_reference = typename TIterator::reference; TIterator begin() const { return {0, std::begin(*Storage_.Ptr())}; } TSentinel end() const { if constexpr (TrivialSentinel) { return TIterator{std::numeric_limits::max(), std::end(*Storage_.Ptr())}; } else { return TSentinel{std::end(*Storage_.Ptr())}; } } mutable TStorage Storage_; }; } //! Usage: for (auto [i, x] : Enumerate(container)) {...} template auto Enumerate(TContainerOrRef&& container) { return NPrivate::TEnumerator{std::forward(container)}; }