// Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #pragma once #include #include #include #include #include #include #include #include #include #include namespace ranges = std::ranges; template constexpr T* nullptr_to = nullptr; template struct borrowed { // borrowed is a borrowed_range; borrowed is not int* begin() const; int* end() const; }; template <> inline constexpr bool ranges::enable_borrowed_range> = true; struct boolish { bool value_ = true; constexpr operator bool() const noexcept { return value_; } [[nodiscard]] constexpr boolish operator!() const noexcept { return {!value_}; } }; template struct holder { static_assert(N < ~std::size_t{0} / sizeof(T)); alignas(T) unsigned char space[N * sizeof(T)]; auto as_span() { return std::span{reinterpret_cast(space + 0), N}; } }; namespace test { using std::assignable_from, std::conditional_t, std::convertible_to, std::copy_constructible, std::derived_from, std::exchange, std::ptrdiff_t, std::span; using output = std::output_iterator_tag; using input = std::input_iterator_tag; using fwd = std::forward_iterator_tag; using bidi = std::bidirectional_iterator_tag; using random = std::random_access_iterator_tag; using contiguous = std::contiguous_iterator_tag; template void operator&(T&&) { static_assert(false); } template void operator,(T&&, U&&) { static_assert(false); } enum class CanDifference : bool { no, yes }; enum class CanCompare : bool { no, yes }; enum class ProxyRef { no, yes, prvalue, xvalue }; enum class WrappedState { wrapped, unwrapped, ignorant, }; template struct prevent_inheriting_unwrap_base { using _Prevent_inheriting_unwrap = Derived; }; template struct prevent_inheriting_unwrap_base {}; [[nodiscard]] constexpr bool is_wrapped(WrappedState s) { return s == WrappedState::wrapped; } template concept compatible_wrapped_state = (W1 == W2) || (W1 == WrappedState::wrapped && W2 == WrappedState::ignorant) || (W1 == WrappedState::ignorant && W2 == WrappedState::wrapped); template [[nodiscard]] constexpr bool to_bool(T const t) noexcept { static_assert(std::is_enum_v && std::same_as, bool>); return static_cast(t); } template class sentinel : public prevent_inheriting_unwrap_base, Wrapped> { Element* ptr_ = nullptr; public: sentinel() = default; constexpr explicit sentinel(Element* ptr) noexcept : ptr_{ptr} {} [[nodiscard]] constexpr Element* peek() const noexcept { return ptr_; } using unwrap = sentinel; using Constinel = sentinel; constexpr operator Constinel() && noexcept { return Constinel{exchange(ptr_, nullptr)}; } constexpr operator Constinel() const& noexcept { return Constinel{ptr_}; } [[nodiscard]] constexpr auto _Unwrapped() const noexcept requires (is_wrapped(Wrapped)) { return unwrap{ptr_}; } static constexpr bool _Unwrap_when_unverified = true; constexpr void _Seek_to(unwrap const& s) noexcept requires (is_wrapped(Wrapped)) { ptr_ = s.peek(); } [[nodiscard]] friend constexpr boolish operator==(sentinel const s, Element* const ptr) noexcept { return {s.ptr_ == ptr}; } [[nodiscard]] friend constexpr boolish operator==(Element* const ptr, sentinel const s) noexcept { return {s.ptr_ == ptr}; } [[nodiscard]] friend constexpr boolish operator!=(sentinel const s, Element* const ptr) noexcept { return !(s == ptr); } [[nodiscard]] friend constexpr boolish operator!=(Element* const ptr, sentinel const s) noexcept { return !(s == ptr); } [[nodiscard]] friend constexpr ptrdiff_t operator-(sentinel const s, Element* const ptr) noexcept { return s.ptr_ - ptr; } [[nodiscard]] friend constexpr ptrdiff_t operator-(Element* const ptr, sentinel const s) noexcept { return ptr - s.ptr_; } }; template concept CanEq = requires(T const& t, U const& u) { { t == u } -> convertible_to; }; template concept CanNEq = requires(T const& t, U const& u) { { t != u } -> convertible_to; }; template concept CanLt = requires(T const& t, U const& u) { { t < u } -> convertible_to; }; template concept CanLtE = requires(T const& t, U const& u) { { t <= u } -> convertible_to; }; template concept CanGt = requires(T const& t, U const& u) { { t > u } -> convertible_to; }; template concept CanGtE = requires(T const& t, U const& u) { { t >= u } -> convertible_to; }; template class proxy_reference { Element& ref_; using Value = std::remove_cv_t; public: constexpr explicit proxy_reference(Element& r) : ref_{r} {} proxy_reference(proxy_reference const&) = default; constexpr proxy_reference const& operator=(proxy_reference const& that) const requires assignable_from { ref_ = that.ref_; return *this; } constexpr operator Element&() const requires derived_from { return ref_; } template requires (!std::same_as, proxy_reference> && assignable_from) constexpr void operator=(T&& val) const { ref_ = std::forward(val); } template [[nodiscard]] constexpr boolish operator==(proxy_reference that) const requires CanEq { return {ref_ == that.peek()}; } template [[nodiscard]] constexpr boolish operator!=(proxy_reference that) const requires CanNEq { return {ref_ != that.peek()}; } template [[nodiscard]] constexpr boolish operator<(proxy_reference that) const requires CanLt { return {ref_ < that.peek()}; } template [[nodiscard]] constexpr boolish operator>(proxy_reference that) const requires CanGt { return {ref_ > that.peek()}; } template [[nodiscard]] constexpr boolish operator<=(proxy_reference that) const requires CanLtE { return {ref_ <= that.peek()}; } template [[nodiscard]] constexpr boolish operator>=(proxy_reference that) const requires CanGtE { return {ref_ >= that.peek()}; } [[nodiscard]] friend constexpr boolish operator==(proxy_reference r, Value const& val) requires CanEq { return {r.ref_ == val}; } [[nodiscard]] friend constexpr boolish operator==(Value const& val, proxy_reference r) requires CanEq { return {r.ref_ == val}; } [[nodiscard]] friend constexpr boolish operator!=(proxy_reference r, Value const& val) requires CanNEq { return {r.ref_ != val}; } [[nodiscard]] friend constexpr boolish operator!=(Value const& val, proxy_reference r) requires CanNEq { return {r.ref_ != val}; } [[nodiscard]] friend constexpr boolish operator<(Value const& val, proxy_reference r) requires CanLt { return {val < r.ref_}; } [[nodiscard]] friend constexpr boolish operator<(proxy_reference r, Value const& val) requires CanLt { return {r.ref_ < val}; } [[nodiscard]] friend constexpr boolish operator>(Value const& val, proxy_reference r) requires CanGt { return {val > r.ref_}; } [[nodiscard]] friend constexpr boolish operator>(proxy_reference r, Value const& val) requires CanGt { return {r.ref_ > val}; } [[nodiscard]] friend constexpr boolish operator<=(Value const& val, proxy_reference r) requires CanLtE { return {val <= r.ref_}; } [[nodiscard]] friend constexpr boolish operator<=(proxy_reference r, Value const& val) requires CanLtE { return {r.ref_ <= val}; } [[nodiscard]] friend constexpr boolish operator>=(Value const& val, proxy_reference r) requires CanGtE { return {val >= r.ref_}; } [[nodiscard]] friend constexpr boolish operator>=(proxy_reference r, Value const& val) requires CanGtE { return {r.ref_ >= val}; } [[nodiscard]] constexpr Element& peek() const noexcept { return ref_; } }; template struct common_reference { Ref ref_; common_reference(Ref r) : ref_{static_cast(r)} {} template requires convertible_to common_reference(proxy_reference pref) : ref_{pref.peek()} {} }; } // namespace test template class TQuals, template class UQuals> requires std::common_reference_with> struct std::basic_common_reference<::test::proxy_reference, U, TQuals, UQuals> { using type = common_reference_t>; }; template class TQuals, template class UQuals> requires std::common_reference_with, Elem&> struct std::basic_common_reference, TQuals, UQuals> { using type = common_reference_t, Elem&>; }; template class TQuals, template class UQuals> requires std::common_reference_with struct std::basic_common_reference<::test::proxy_reference, ::test::proxy_reference, TQuals, UQuals> { using type = common_reference_t; }; namespace test { template struct init_list_not_constructible_sentinel { init_list_not_constructible_sentinel() = default; init_list_not_constructible_sentinel(T*) {} template init_list_not_constructible_sentinel(std::initializer_list) = delete; }; template struct init_list_not_constructible_iterator { using value_type = T; using difference_type = int; init_list_not_constructible_iterator() = default; init_list_not_constructible_iterator(T*) {} template init_list_not_constructible_iterator(std::initializer_list) = delete; T& operator*() const; // not defined init_list_not_constructible_iterator& operator++(); // not defined init_list_not_constructible_iterator operator++(int); // not defined bool operator==(init_list_not_constructible_iterator) const; // not defined bool operator==(init_list_not_constructible_sentinel) const; // not defined }; static_assert(std::forward_iterator>); static_assert( std::sentinel_for, init_list_not_constructible_iterator>); template }, // Model sentinel_for with self (and sized_sentinel_for if Diff implies copyable)? CanCompare Eq = CanCompare{derived_from}, // Use a ProxyRef reference type (instead of Element&)? ProxyRef Proxy = ProxyRef{!derived_from}, // Interact with the STL's iterator unwrapping machinery? WrappedState Wrapped = WrappedState::wrapped> requires (to_bool(Eq) || !derived_from) && (Proxy == ProxyRef::no || !derived_from) class iterator : public prevent_inheriting_unwrap_base, Wrapped> { Element* ptr_; template static constexpr bool at_least = derived_from; using ReferenceType = conditional_t, conditional_t, conditional_t>>; struct post_increment_proxy { Element* ptr_; const post_increment_proxy& operator*() const noexcept { return *this; } template requires std::indirectly_writable const post_increment_proxy& operator=(T&& t) const noexcept { *ptr_ = std::forward(t); return *this; } }; public: using Consterator = iterator; // output iterator operations iterator() requires at_least || (Eq == CanCompare::yes) = default; constexpr explicit iterator(Element* ptr) noexcept : ptr_{ptr} {} constexpr iterator(iterator&& that) noexcept : ptr_{exchange(that.ptr_, nullptr)} {} constexpr iterator& operator=(iterator&& that) noexcept { ptr_ = exchange(that.ptr_, nullptr); return *this; } constexpr operator Consterator() && noexcept { return Consterator{exchange(ptr_, nullptr)}; } [[nodiscard]] constexpr Element* peek() const noexcept { return ptr_; } [[nodiscard]] constexpr ReferenceType operator*() const noexcept { return static_cast(*ptr_); } template [[nodiscard]] friend constexpr boolish operator==( iterator const& i, sentinel const& s) noexcept requires compatible_wrapped_state { return boolish{i.peek() == s.peek()}; } template [[nodiscard]] friend constexpr boolish operator==( sentinel const& s, iterator const& i) noexcept requires compatible_wrapped_state { return i == s; } template [[nodiscard]] friend constexpr boolish operator!=( iterator const& i, sentinel const& s) noexcept requires compatible_wrapped_state { return !(i == s); } template [[nodiscard]] friend constexpr boolish operator!=( sentinel const& s, iterator const& i) noexcept requires compatible_wrapped_state { return !(i == s); } constexpr iterator& operator++() & noexcept { ++ptr_; return *this; } constexpr post_increment_proxy operator++(int) & noexcept requires std::is_same_v { post_increment_proxy result{ptr_}; ++ptr_; return result; } auto operator--() & { static_assert(false); } auto operator--(int) & { static_assert(false); } friend void iter_swap(iterator const&, iterator const&) requires std::is_same_v { static_assert(false); } void operator<(iterator const&) const { static_assert(false); } void operator>(iterator const&) const { static_assert(false); } void operator<=(iterator const&) const { static_assert(false); } void operator>=(iterator const&) const { static_assert(false); } // input iterator operations: constexpr void operator++(int) & noexcept requires std::is_same_v { ++ptr_; } [[nodiscard]] friend constexpr Element&& iter_move(iterator const& i) requires at_least { return std::move(*i.ptr_); } friend constexpr void iter_swap(iterator const& x, iterator const& y) noexcept(std::is_nothrow_swappable_v) requires at_least && std::swappable { ranges::swap(*x.ptr_, *y.ptr_); } // forward iterator operations: constexpr iterator operator++(int) & noexcept requires at_least { auto tmp = *this; ++ptr_; return tmp; } // sentinel operations (implied by forward iterator): iterator(iterator const&) requires (to_bool(Eq)) = default; iterator& operator=(iterator const&) requires (to_bool(Eq)) = default; constexpr operator Consterator() const& noexcept requires (to_bool(Eq)) { return Consterator{ptr_}; } [[nodiscard]] constexpr boolish operator==(iterator const& that) const noexcept requires (to_bool(Eq)) { return {ptr_ == that.ptr_}; } [[nodiscard]] constexpr boolish operator!=(iterator const& that) const noexcept requires (to_bool(Eq)) { return !(*this == that); } // bidi iterator operations: constexpr iterator& operator--() & noexcept requires at_least { --ptr_; return *this; } constexpr iterator operator--(int) & noexcept requires at_least { auto tmp = *this; --ptr_; return tmp; } // random-access iterator operations: [[nodiscard]] constexpr boolish operator<(iterator const& that) const noexcept requires at_least { return {ptr_ < that.ptr_}; } [[nodiscard]] constexpr boolish operator>(iterator const& that) const noexcept requires at_least { return that < *this; } [[nodiscard]] constexpr boolish operator<=(iterator const& that) const noexcept requires at_least { return !(that < *this); } [[nodiscard]] constexpr boolish operator>=(iterator const& that) const noexcept requires at_least { return !(*this < that); } [[nodiscard]] constexpr auto operator<=>(iterator const& that) const noexcept requires at_least { return ptr_ <=> that.ptr_; } [[nodiscard]] constexpr ReferenceType operator[](ptrdiff_t const n) const& noexcept requires at_least { return ReferenceType{ptr_[n]}; } constexpr iterator& operator+=(ptrdiff_t const n) & noexcept requires at_least { ptr_ += n; return *this; } constexpr iterator& operator-=(ptrdiff_t const n) & noexcept requires at_least { ptr_ -= n; return *this; } [[nodiscard]] constexpr iterator operator+(ptrdiff_t const n) const noexcept requires at_least { return iterator{ptr_ + n}; } [[nodiscard]] friend constexpr iterator operator+(ptrdiff_t const n, iterator const& i) noexcept requires at_least { return i + n; } [[nodiscard]] constexpr iterator operator-(ptrdiff_t const n) const noexcept requires at_least { return iterator{ptr_ - n}; } // contiguous iterator operations: [[nodiscard]] constexpr Element* operator->() const noexcept requires at_least { return ptr_; } // sized_sentinel_for operations: [[nodiscard]] constexpr ptrdiff_t operator-(iterator const& that) const noexcept requires at_least || (to_bool(Diff) && to_bool(Eq)) { return ptr_ - that.ptr_; } template [[nodiscard]] constexpr ptrdiff_t operator-(sentinel const& s) const noexcept requires compatible_wrapped_state && (to_bool(Diff)) { return ptr_ - s.peek(); } template [[nodiscard]] friend constexpr ptrdiff_t operator-( sentinel const& s, iterator const& i) noexcept requires compatible_wrapped_state && (to_bool(Diff)) { return -(i - s); } using unwrap = std::conditional_t, Element*, iterator>; using unwrapping_ignorant = iterator; [[nodiscard]] constexpr auto _Unwrapped() const& noexcept requires (is_wrapped(Wrapped) && to_bool(Eq)) { return unwrap{ptr_}; } [[nodiscard]] constexpr auto _Unwrapped() && noexcept requires (is_wrapped(Wrapped)) { return unwrap{exchange(ptr_, nullptr)}; } static constexpr bool _Unwrap_when_unverified = true; constexpr void _Seek_to(unwrap const& i) noexcept requires (is_wrapped(Wrapped) && to_bool(Eq)) { if constexpr (at_least) { ptr_ = i; } else { ptr_ = i.peek(); } } constexpr void _Seek_to(unwrap&& i) noexcept requires (is_wrapped(Wrapped)) { if constexpr (at_least) { ptr_ = i; } else { ptr_ = i.peek(); } } }; template struct iterator_traits_base {}; template struct iterator_traits_base { using iterator_category = Category; }; template struct iterator_traits_base { using iterator_category = input; }; template struct iterator_traits_base { using iterator_category = input; }; } // namespace test template struct std::iterator_traits<::test::iterator> : ::test::iterator_traits_base, Proxy == ::test::ProxyRef::yes, Eq == ::test::CanCompare::yes> { using iterator_concept = Category; using value_type = remove_cv_t; using difference_type = ptrdiff_t; using pointer = conditional_t, Element*, void>; using reference = iter_reference_t<::test::iterator>; }; template struct std::pointer_traits<::test::iterator> { using pointer = ::test::iterator; using element_type = Element; using difference_type = ptrdiff_t; [[nodiscard]] static constexpr element_type* to_address(pointer const& x) noexcept { return x.peek(); } }; namespace test { enum class Sized : bool { no, yes }; enum class Common : bool { no, yes }; enum class CanView : bool { no, yes }; enum class Copyability { immobile, move_only, copyable }; namespace detail { template class range_base { public: static_assert(Copy == Copyability::immobile); range_base() = delete; constexpr explicit range_base(span elements) noexcept : elements_{elements} {} range_base(const range_base&) = delete; range_base& operator=(const range_base&) = delete; protected: [[nodiscard]] constexpr bool moved_from() const noexcept { return false; } span elements_; }; template class range_base { public: range_base() = delete; constexpr explicit range_base(span elements) noexcept : elements_{elements} {} constexpr range_base(range_base&& that) noexcept : elements_{that.elements_}, moved_from_{that.moved_from_} { that.elements_ = {}; that.moved_from_ = true; } constexpr range_base& operator=(range_base&& that) noexcept { elements_ = that.elements_; moved_from_ = that.moved_from_; that.elements_ = {}; that.moved_from_ = true; return *this; } protected: [[nodiscard]] constexpr bool moved_from() const noexcept { return moved_from_; } span elements_; private: bool moved_from_ = false; }; template class range_base { public: constexpr range_base() = default; constexpr explicit range_base(span elements) noexcept : elements_{elements} {} constexpr range_base(const range_base&) = default; constexpr range_base& operator=(const range_base&) = default; constexpr range_base(range_base&& that) noexcept : elements_{that.elements_}, moved_from_{that.moved_from_} { that.elements_ = {}; that.moved_from_ = true; } constexpr range_base& operator=(range_base&& that) noexcept { elements_ = that.elements_; moved_from_ = that.moved_from_; that.elements_ = {}; that.moved_from_ = true; return *this; } protected: [[nodiscard]] constexpr bool moved_from() const noexcept { return moved_from_; } span elements_; private: bool moved_from_ = false; }; } // namespace detail template }, // Model common_range? Common IsCommon = Common::no, // Iterator models sentinel_for with self CanCompare Eq = CanCompare{derived_from}, // Use a ProxyRef reference type? ProxyRef Proxy = ProxyRef{!derived_from}, // Should this range satisfy the view concept? CanView IsView = CanView::no, // Should this range type be copyable/movable/neither? Copyability Copy = IsView == CanView::yes ? Copyability::move_only : Copyability::immobile> requires (!to_bool(IsCommon) || to_bool(Eq)) && (to_bool(Eq) || !derived_from) && (Proxy == ProxyRef::no || !derived_from) && (!to_bool(IsView) || Copy != Copyability::immobile) class range : public detail::range_base { private: mutable bool begin_called_ = false; using detail::range_base::elements_; using detail::range_base::moved_from; public: using I = iterator; using S = conditional_t>; using RebindAsMoveOnly = range; template using RebindElement = range; static constexpr ProxyRef proxy_ref = Proxy; using detail::range_base::range_base; [[nodiscard]] constexpr I begin() const noexcept { assert(!moved_from()); if constexpr (!derived_from) { assert(!exchange(begin_called_, true)); } return I{elements_.data()}; } [[nodiscard]] constexpr S end() const noexcept { assert(!moved_from()); return S{elements_.data() + elements_.size()}; } [[nodiscard]] constexpr ptrdiff_t size() const noexcept requires (to_bool(IsSized)) { assert(!moved_from()); if constexpr (!derived_from) { assert(!begin_called_); } return static_cast(elements_.size()); } [[nodiscard]] constexpr Element* data() const noexcept requires derived_from { assert(!moved_from()); return elements_.data(); } using UI = I::unwrap; using US = conditional_t>; [[nodiscard]] constexpr UI _Unchecked_begin() const noexcept { assert(!moved_from()); if constexpr (!derived_from) { assert(!exchange(begin_called_, true)); } return UI{elements_.data()}; } [[nodiscard]] constexpr US _Unchecked_end() const noexcept { assert(!moved_from()); return US{elements_.data() + elements_.size()}; } void operator&() const { static_assert(false); } template friend void operator,(range const&, T&&) { static_assert(false); } }; template [[nodiscard]] constexpr auto to_unsigned(I n) noexcept { if constexpr (std::signed_integral) { return static_cast>(n); } else { return static_cast(n); } } template struct redifference_iterator_category_base {}; template requires std::signed_integral && requires { typename std::iterator_traits::iterator_category; } struct redifference_iterator_category_base { using iterator_category = std::iterator_traits::iterator_category; }; template class redifference_iterator : public redifference_iterator_category_base { public: using iterator_concept = decltype([] { if constexpr (std::contiguous_iterator) { return std::contiguous_iterator_tag{}; } else if constexpr (std::random_access_iterator) { return std::random_access_iterator_tag{}; } else if constexpr (std::bidirectional_iterator) { return std::bidirectional_iterator_tag{}; } else if constexpr (std::forward_iterator) { return std::forward_iterator_tag{}; } else { return std::input_iterator_tag{}; } }()); using value_type = std::iter_value_t; using difference_type = Diff; redifference_iterator() = default; constexpr explicit redifference_iterator(It it) : i_{std::move(it)} {} [[nodiscard]] constexpr decltype(auto) operator*() const { return *i_; } constexpr decltype(auto) operator->() const requires std::contiguous_iterator || (requires(const It& i) { i.operator->(); }) { if constexpr (std::contiguous_iterator) { return std::to_address(i_); } else { return i_.operator->(); } } constexpr redifference_iterator& operator++() { ++i_; return *this; } constexpr decltype(auto) operator++(int) { if constexpr (std::is_same_v) { return redifference_iterator{i_++}; } else { return i_++; } } constexpr redifference_iterator& operator--() requires std::bidirectional_iterator { --i_; return *this; } constexpr redifference_iterator operator--(int) requires std::bidirectional_iterator { return redifference_iterator{--i_}; } constexpr redifference_iterator& operator+=(std::same_as auto n) requires std::random_access_iterator { i_ += static_cast>(n); return *this; } constexpr redifference_iterator& operator-=(std::same_as auto n) requires std::random_access_iterator { i_ -= static_cast>(n); return *this; } [[nodiscard]] constexpr decltype(auto) operator[](std::same_as auto n) const requires std::random_access_iterator { return i_[static_cast>(n)]; } [[nodiscard]] friend constexpr bool operator==(const redifference_iterator& i, const redifference_iterator& j) { return i.i_ == j.i_; } template I> // TRANSITION, DevCom-10735214, should be abbreviated [[nodiscard]] friend constexpr redifference_iterator operator+(const redifference_iterator& it, I n) requires std::random_access_iterator { return redifference_iterator{it.i_ + static_cast>(n)}; } template I> // TRANSITION, DevCom-10735214, should be abbreviated [[nodiscard]] friend constexpr redifference_iterator operator+(I n, const redifference_iterator& it) requires std::random_access_iterator { return redifference_iterator{it.i_ + static_cast>(n)}; } template I> // TRANSITION, DevCom-10735214, should be abbreviated [[nodiscard]] friend constexpr redifference_iterator operator-(const redifference_iterator& it, I n) requires std::random_access_iterator { return redifference_iterator{it.i_ - static_cast>(n)}; } [[nodiscard]] friend constexpr difference_type operator-( const redifference_iterator& i, const redifference_iterator& j) requires std::random_access_iterator { return static_cast(i.i_ - j.i_); } [[nodiscard]] friend constexpr auto operator<=>(const redifference_iterator& i, const redifference_iterator& j) requires std::random_access_iterator { if constexpr (std::three_way_comparable) { return i.i_ <=> j.i_; } else { if (i.i_ < j.i_) { return std::weak_ordering::less; } else if (j.i_ < i.i_) { return std::weak_ordering::greater; } else { return std::weak_ordering::equivalent; } } } [[nodiscard]] friend constexpr bool operator<(const redifference_iterator& i, const redifference_iterator& j) requires std::random_access_iterator { return i.i_ < j.i_; } [[nodiscard]] friend constexpr bool operator>(const redifference_iterator& i, const redifference_iterator& j) requires std::random_access_iterator { return j.i_ < i.i_; } [[nodiscard]] friend constexpr bool operator<=(const redifference_iterator& i, const redifference_iterator& j) requires std::random_access_iterator { return !(j.i_ < i.i_); } [[nodiscard]] friend constexpr bool operator>=(const redifference_iterator& i, const redifference_iterator& j) requires std::random_access_iterator { return !(i.i_ < j.i_); } [[nodiscard]] constexpr const It& base() const noexcept { return i_; } private: It i_; }; template struct redifference_sentinel { S se_; template requires std::sentinel_for [[nodiscard]] friend constexpr bool operator==( const redifference_iterator& i, const redifference_sentinel& s) { return i.base() == s.se_; } template requires std::sized_sentinel_for [[nodiscard]] friend constexpr Diff operator-( const redifference_iterator& i, const redifference_sentinel& s) { return static_cast(i.base() - s.se_); } template requires std::sized_sentinel_for [[nodiscard]] friend constexpr Diff operator-( const redifference_sentinel& s, const redifference_iterator& i) { return static_cast(s.se_ - i.base()); } }; template [[nodiscard]] constexpr auto make_redifference_subrange(Rng&& r) { constexpr bool is_sized = ranges::sized_range || std::sized_sentinel_for, ranges::iterator_t>; using rediff_iter = redifference_iterator>; using rediff_sent = redifference_sentinel>; if constexpr (is_sized) { const auto sz = to_unsigned(static_cast(ranges::distance(r))); return ranges::subrange{ rediff_iter{ranges::begin(r)}, rediff_sent{ranges::end(r)}, sz}; } else { return ranges::subrange{ rediff_iter{ranges::begin(r)}, rediff_sent{ranges::end(r)}}; } } } // namespace test template constexpr bool std::ranges::enable_view< test::range> = true; template class basic_borrowed_range : public test::range { using test::range::range; }; template basic_borrowed_range(R&) -> basic_borrowed_range>>; template constexpr bool ranges::enable_borrowed_range<::basic_borrowed_range> = true; template struct unique_tag {}; template using ProjectionFor = unique_tag (*)(std::iter_common_reference_t); template using UnaryPredicateFor = boolish (*)(std::iter_common_reference_t); template using ProjectedUnaryPredicate = boolish (*)(unique_tag); template using HalfProjectedBinaryPredicateFor = boolish (*)(unique_tag, std::iter_common_reference_t); template using ProjectedBinaryPredicate = boolish (*)(unique_tag, unique_tag); template using BinaryPredicateFor = boolish (*)(std::iter_common_reference_t, std::iter_common_reference_t); template struct with_output_iterators { template static constexpr void call() { using namespace test; using test::iterator; // Diff and Eq are not significant for "lone" single-pass iterators, so we can ignore them here. Continuation::template call>(); Continuation::template call>(); // For forward and bidi, Eq is necessarily true but Diff and Proxy may vary. Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); // Random iterators are Diff and Eq - only Proxy varies. Continuation::template call>(); Continuation::template call>(); // Contiguous iterators are totally locked down. Continuation::template call>(); } }; template struct with_writable_iterators { template static constexpr bool call() { using namespace test; using test::iterator; // Diff and Eq are not significant for "lone" single-pass iterators, so we can ignore them here. Continuation::template call>(); Continuation::template call>(); with_output_iterators::template call(); return true; } }; template struct with_contiguous_ranges { template static constexpr void call() { using namespace test; using test::range; // Ditto always Eq; !IsSized && SizedSentinel is uninteresting (ranges::size still works), as is // !IsSized && IsCommon. contiguous also implies !Proxy. Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); } }; template struct with_random_ranges { template static constexpr void call() { using namespace test; using test::range; // Ditto always Eq; !IsSized && SizedSentinel is uninteresting (ranges::size works either way), as is // !IsSized && IsCommon. Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); with_contiguous_ranges::template call(); } }; template struct with_bidirectional_ranges { template static constexpr void call() { using namespace test; using test::range; // Ditto always Eq; !IsSized && Diff is uninteresting (ranges::size still works). Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); with_random_ranges::template call(); } }; template struct with_forward_ranges { template static constexpr void call() { using namespace test; using test::range; // forward always has Eq; !IsSized && Diff is uninteresting (sized_range is sized_range). Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); with_bidirectional_ranges::template call(); } }; template struct with_input_ranges { template static constexpr void call() { using namespace test; using test::range; // For all ranges, IsCommon implies Eq. // For single-pass ranges, Eq is uninteresting without IsCommon (there's only one valid iterator // value at a time, and no reason to compare it with itself for equality). Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); with_forward_ranges::template call(); } }; template struct with_output_ranges { template static constexpr void call() { using namespace test; using test::range; // For all ranges, IsCommon implies Eq. // For single-pass ranges, Eq is uninteresting without IsCommon (there's only one valid iterator // value at a time, and no reason to compare it with itself for equality). Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); with_forward_ranges::template call(); } }; template struct with_input_or_output_ranges { template static constexpr void call() { using namespace test; using test::range; // For all ranges, IsCommon implies Eq. // For single-pass ranges, Eq is uninteresting without IsCommon (there's only one valid iterator // value at a time, and no reason to compare it with itself for equality). Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); with_output_ranges::template call(); } }; template struct with_input_iterators { template static constexpr void call() { using namespace test; using test::iterator; // IsSized and Eq are not significant for "lone" single-pass iterators, so we can ignore them here. Continuation::template call>(); Continuation::template call>(); // For forward and bidi, Eq is necessarily true but IsSized and Proxy may vary. Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); Continuation::template call>(); // Random iterators are IsSized and Eq - only Proxy varies. Continuation::template call>(); Continuation::template call>(); // Contiguous iterators are totally locked down. Continuation::template call>(); } }; template constexpr void test_out() { with_output_ranges::call(); } template constexpr void test_in() { with_input_ranges::call(); } template constexpr void test_inout() { with_input_or_output_ranges::call(); } template constexpr void test_fwd() { with_forward_ranges::call(); } template constexpr void test_bidi() { with_bidirectional_ranges::call(); } template constexpr void test_random() { with_random_ranges::call(); } template constexpr void test_contiguous() { with_contiguous_ranges::call(); } template constexpr void test_in_in() { with_input_ranges, Element1>::call(); } template constexpr void test_in_fwd() { with_input_ranges, Element1>::call(); } template constexpr void test_in_random() { with_input_ranges, Element1>::call(); } template constexpr void test_fwd_fwd() { with_forward_ranges, Element1>::call(); } template constexpr void test_bidi_bidi() { with_bidirectional_ranges, Element1>::call(); } template constexpr void input_range_output_iterator_permutations() { with_input_ranges, Element1>::call(); } template constexpr void test_in_write() { with_input_ranges, Element1>::call(); } template constexpr void test_fwd_write() { with_forward_ranges, Element1>::call(); } template constexpr void test_bidi_write() { with_bidirectional_ranges, Element1>::call(); } template constexpr void test_contiguous_write() { with_contiguous_ranges, Element1>::call(); } template constexpr void test_read() { with_input_iterators::call(); } template constexpr void test_read_write() { with_input_iterators, Element1>::call(); } template constexpr void test_in_in_write() { with_input_ranges, Element2>, Element1>::call(); } template struct get_nth_fn { template [[nodiscard]] constexpr auto&& operator()(T&& t) const noexcept requires requires { get(std::forward(t)); } { return get(std::forward(t)); } template [[nodiscard]] constexpr decltype(auto) operator()(test::proxy_reference r) const noexcept requires requires { (*this)(r.peek()); } { return (*this)(r.peek()); } }; inline constexpr get_nth_fn<0> get_first; inline constexpr get_nth_fn<1> get_second; template concept CanBegin = requires(R&& r) { ranges::begin(std::forward(r)); }; template concept CanMemberBegin = requires(R&& r) { std::forward(r).begin(); }; template concept CanEnd = requires(R&& r) { ranges::end(std::forward(r)); }; template concept CanMemberEnd = requires(R&& r) { std::forward(r).end(); }; template concept CanCBegin = requires(R&& r) { ranges::cbegin(std::forward(r)); }; template concept CanMemberCBegin = requires(R&& r) { std::forward(r).cbegin(); }; template concept CanCEnd = requires(R&& r) { ranges::cend(std::forward(r)); }; template concept CanMemberCEnd = requires(R&& r) { std::forward(r).cend(); }; template concept CanRBegin = requires(R&& r) { ranges::rbegin(std::forward(r)); }; template concept CanREnd = requires(R&& r) { ranges::rend(std::forward(r)); }; template concept CanCRBegin = requires(R&& r) { ranges::crbegin(std::forward(r)); }; template concept CanCREnd = requires(R&& r) { ranges::crend(std::forward(r)); }; template concept CanEmpty = requires(R&& r) { ranges::empty(std::forward(r)); }; template concept CanSize = requires(R&& r) { ranges::size(std::forward(r)); }; template concept CanMemberSize = requires(R&& r) { std::forward(r).size(); }; template concept CanSSize = requires(R&& r) { ranges::ssize(std::forward(r)); }; template concept CanData = requires(R&& r) { ranges::data(std::forward(r)); }; template concept CanMemberData = requires(R&& r) { std::forward(r).data(); }; template concept CanCData = requires(R&& r) { ranges::cdata(std::forward(r)); }; template concept CanMemberBase = requires(T&& t) { std::forward(t).base(); }; template concept CanMemberEmpty = requires(R&& r) { std::forward(r).empty(); }; template concept CanMemberFront = requires(R&& r) { std::forward(r).front(); }; template concept CanMemberBack = requires(R&& r) { std::forward(r).back(); }; template concept CanIndex = requires(R&& r, const ranges::range_difference_t i) { std::forward(r)[i]; }; template concept CanBool = requires(R&& r) { std::forward(r) ? true : false; }; template concept CanIterSwap = requires(I&& i1, I&& i2) { ranges::iter_swap(std::forward(i1), std::forward(i2)); };