1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14, c++17 10 // UNSUPPORTED: libcpp-has-no-incomplete-ranges 11 12 // friend constexpr decltype(auto) iter_move(const inner-iterator& i) 13 // noexcept(noexcept(ranges::iter_move(i.i_.<current>))); 14 15 #include <iterator> 16 17 #include <cassert> 18 #include <type_traits> 19 #include <utility> 20 #include "../types.h" 21 22 namespace adl { 23 24 template <bool IsNoexcept = false> 25 struct MaybeNoexceptIterator { 26 using value_type = int; 27 using difference_type = ptrdiff_t; 28 29 value_type* ptr_ = nullptr; 30 int* iter_move_invocations_ = nullptr; 31 32 constexpr MaybeNoexceptIterator() = default; 33 constexpr explicit MaybeNoexceptIterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {} 34 35 constexpr value_type& operator*() const { return *ptr_; } 36 37 MaybeNoexceptIterator& operator++() { ++ptr_; return *this; } 38 MaybeNoexceptIterator operator++(int) { 39 MaybeNoexceptIterator prev = *this; 40 ++ptr_; 41 return prev; 42 } 43 44 constexpr MaybeNoexceptIterator& operator--() { --ptr_; return *this; } 45 constexpr MaybeNoexceptIterator operator--(int) { 46 MaybeNoexceptIterator prev = *this; 47 --ptr_; 48 return prev; 49 } 50 51 constexpr friend value_type&& iter_move(MaybeNoexceptIterator iter) noexcept(IsNoexcept) { 52 if (iter.iter_move_invocations_) { 53 ++(*iter.iter_move_invocations_); 54 } 55 return std::move(*iter); 56 } 57 58 friend bool operator==(const MaybeNoexceptIterator& lhs, const MaybeNoexceptIterator& rhs) { return lhs.ptr_ == rhs.ptr_; } 59 }; 60 61 template <bool IsNoexcept = false> 62 struct View : std::ranges::view_base { 63 static constexpr int N = 3; 64 int a[N] = {0, 1, 2}; 65 int* iter_moves = nullptr; 66 67 constexpr View() = default; 68 constexpr View(int& iter_move_invocations) : iter_moves(&iter_move_invocations) { 69 } 70 71 constexpr adl::MaybeNoexceptIterator<IsNoexcept> begin() { 72 return adl::MaybeNoexceptIterator<IsNoexcept>(a, *iter_moves); 73 } 74 constexpr adl::MaybeNoexceptIterator<IsNoexcept> end() { 75 return adl::MaybeNoexceptIterator<IsNoexcept>(a + N, *iter_moves); 76 } 77 }; 78 79 } // namespace adl 80 81 constexpr bool test() { 82 // Can use `iter_move` with `inner-iterator`; `View` is a forward range. 83 { 84 SplitViewForward v("abc def", " "); 85 auto segment = *v.begin(); 86 87 // Non-const iterator. 88 { 89 auto i = segment.begin(); 90 static_assert(std::same_as<decltype(iter_move(i)), const char &&>); 91 assert(iter_move(i) == 'a'); 92 } 93 94 // Const iterator. 95 { 96 const auto i = segment.begin(); 97 static_assert(std::same_as<decltype(iter_move(i)), const char &&>); 98 assert(iter_move(i) == 'a'); 99 } 100 } 101 102 // Can use `iter_move` with `inner-iterator`, `View` is an input range. 103 { 104 SplitViewInput v("abc def", ' '); 105 auto segment = *v.begin(); 106 107 // Non-const iterator. 108 { 109 auto i = segment.begin(); 110 static_assert(std::same_as<decltype(iter_move(i)), char &&>); 111 assert(iter_move(i) == 'a'); 112 } 113 114 // Const iterator. 115 { 116 const auto i = segment.begin(); 117 static_assert(std::same_as<decltype(iter_move(i)), char &&>); 118 assert(iter_move(i) == 'a'); 119 } 120 } 121 122 // Ensure the `iter_move` customization point is being used. 123 { 124 int iter_move_invocations = 0; 125 adl::View<> input(iter_move_invocations); 126 std::ranges::lazy_split_view<adl::View<>, adl::View<>> v(input, adl::View<>()); 127 128 auto segment = *v.begin(); 129 auto i = segment.begin(); 130 int x = iter_move(i); 131 assert(x == 0); 132 assert(iter_move_invocations == 1); 133 } 134 135 // Check the `noexcept` specification. 136 { 137 { 138 using ThrowingSplitView = std::ranges::lazy_split_view<adl::View<false>, adl::View<false>>; 139 using ThrowingValueType = std::ranges::iterator_t<ThrowingSplitView>::value_type; 140 using ThrowingIter = std::ranges::iterator_t<ThrowingValueType>; 141 ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval<adl::MaybeNoexceptIterator<false>>())); 142 ASSERT_NOT_NOEXCEPT(iter_move(std::declval<ThrowingIter>())); 143 } 144 145 { 146 using NoexceptSplitView = std::ranges::lazy_split_view<adl::View<true>, adl::View<true>>; 147 using NoexceptValueType = std::ranges::iterator_t<NoexceptSplitView>::value_type; 148 using NoexceptIter = std::ranges::iterator_t<NoexceptValueType>; 149 ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval<adl::MaybeNoexceptIterator<true>>())); 150 ASSERT_NOEXCEPT(iter_move(std::declval<NoexceptIter>())); 151 } 152 } 153 154 return true; 155 } 156 157 int main(int, char**) { 158 test(); 159 static_assert(test()); 160 161 return 0; 162 } 163