#pragma once #include #include #include namespace NPrivate { template struct TConcatenator { template struct TConcatenatorWithIndex { private: using THolders = std::tuple...>; using TValue = TValue_; using TIteratorState = std::tuple()))...>; using TSentinelState = std::tuple()))...>; struct TIterator; struct TSentinelCandidate { TSentinelState Iterators_; std::size_t Position_; THolders* HoldersPtr_; }; using TSentinel = std::conditional_t, TIterator, TSentinelCandidate>; struct TIterator { private: friend struct TConcatenatorWithIndex; // important, that it is a static function, compiler better optimizes such code template static TValue GetCurrentValue(std::size_t position, TMaybeConstIteratorState& iterators) { if constexpr (index >= sizeof...(TContainers)) { // never happened when use of iterator is correct return *std::get<0>(iterators); } else { if (position == index) { return *std::get(iterators); } else { return GetCurrentValue(position, iterators); } } } template void MaybeIncrementIteratorAndSkipExhaustedContainers() { if constexpr (index >= sizeof...(TContainers)) { return; } else { if (Position_ == index) { if constexpr (needIncrement) { ++std::get(Iterators_); } if (!(std::get(Iterators_) != std::end(*std::get(*HoldersPtr_).Ptr()))) { ++Position_; MaybeIncrementIteratorAndSkipExhaustedContainers(); } } else { MaybeIncrementIteratorAndSkipExhaustedContainers(); } } } public: using difference_type = std::ptrdiff_t; using value_type = TValue; using pointer = std::remove_reference_t*; using reference = std::remove_reference_t&; using iterator_category = std::input_iterator_tag; TValue operator*() { return GetCurrentValue(Position_, Iterators_); } TValue operator*() const { return GetCurrentValue(Position_, Iterators_); } TIterator& operator++() { MaybeIncrementIteratorAndSkipExhaustedContainers(); return *this; } bool operator!=(const TSentinel& other) const { // give compiler an opportunity to optimize sentinel case (-70% of time) if (other.Position_ == sizeof...(TContainers)) { return Position_ < sizeof...(TContainers); } else { return (Position_ != other.Position_ || ((std::get(Iterators_) != std::get(other.Iterators_)) || ...)); } } bool operator==(const TSentinel& other) const { return !(*this != other); } TIteratorState Iterators_; std::size_t Position_; THolders* HoldersPtr_; }; 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 { TIterator iterator{TIteratorState{std::begin(*std::get(Holders_).Ptr())...}, 0, &Holders_}; iterator.template MaybeIncrementIteratorAndSkipExhaustedContainers(); return iterator; } TSentinel end() const { return {TSentinelState{std::end(*std::get(Holders_).Ptr())...}, sizeof...(TContainers), &Holders_}; } mutable THolders Holders_; }; template static auto Concatenate(TContainers&&... containers, std::index_sequence) { return TConcatenatorWithIndex{{std::forward(containers)...}}; } }; } //! Usage: for (auto x : Concatenate(a, b)) {...} template auto Concatenate(TFirstContainer&& container, TContainers&&... containers) { return NPrivate::TConcatenator::Concatenate( std::forward(container), std::forward(containers)..., std::make_index_sequence{}); }