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 Iterator { 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 Iterator() = default; 33 constexpr explicit Iterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {} 34 35 constexpr value_type& operator*() const { return *ptr_; } 36 37 Iterator& operator++() { ++ptr_; return *this; } 38 Iterator operator++(int) { 39 Iterator prev = *this; 40 ++ptr_; 41 return prev; 42 } 43 44 constexpr Iterator& operator--() { --ptr_; return *this; } 45 constexpr Iterator operator--(int) { 46 Iterator prev = *this; 47 --ptr_; 48 return prev; 49 } 50 51 constexpr friend value_type&& iter_move(Iterator 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 Iterator& lhs, const Iterator& 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::Iterator<IsNoexcept> begin() { return adl::Iterator<IsNoexcept>(a, *iter_moves); } 72 constexpr adl::Iterator<IsNoexcept> end() { return adl::Iterator<IsNoexcept>(a + N, *iter_moves); } 73 }; 74 75 } // namespace adl 76 77 constexpr bool test() { 78 // Can use `iter_move` with `inner-iterator`; `View` is a forward range. 79 { 80 SplitViewForward v("abc def", " "); 81 auto segment = *v.begin(); 82 83 // Non-const iterator. 84 { 85 auto i = segment.begin(); 86 static_assert(std::same_as<decltype(iter_move(i)), const char &&>); 87 assert(iter_move(i) == 'a'); 88 } 89 90 // Const iterator. 91 { 92 const auto i = segment.begin(); 93 static_assert(std::same_as<decltype(iter_move(i)), const char &&>); 94 assert(iter_move(i) == 'a'); 95 } 96 } 97 98 // Can use `iter_move` with `inner-iterator`, `View` is an input range. 99 { 100 SplitViewInput v("abc def", ' '); 101 auto segment = *v.begin(); 102 103 // Non-const iterator. 104 { 105 auto i = segment.begin(); 106 static_assert(std::same_as<decltype(iter_move(i)), char &&>); 107 assert(iter_move(i) == 'a'); 108 } 109 110 // Const iterator. 111 { 112 const auto i = segment.begin(); 113 static_assert(std::same_as<decltype(iter_move(i)), char &&>); 114 assert(iter_move(i) == 'a'); 115 } 116 } 117 118 // Ensure the `iter_move` customization point is being used. 119 { 120 int iter_move_invocations = 0; 121 adl::View<> input(iter_move_invocations); 122 std::ranges::lazy_split_view<adl::View<>, adl::View<>> v(input, adl::View<>()); 123 124 auto segment = *v.begin(); 125 auto i = segment.begin(); 126 int x = iter_move(i); 127 assert(x == 0); 128 assert(iter_move_invocations == 1); 129 } 130 131 // Check the `noexcept` specification. 132 { 133 { 134 using ThrowingSplitView = std::ranges::lazy_split_view<adl::View<false>, adl::View<false>>; 135 using ThrowingValueType = std::ranges::iterator_t<ThrowingSplitView>::value_type; 136 using ThrowingIter = std::ranges::iterator_t<ThrowingValueType>; 137 ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval<adl::Iterator<false>>())); 138 ASSERT_NOT_NOEXCEPT(iter_move(std::declval<ThrowingIter>())); 139 } 140 141 { 142 using NoexceptSplitView = std::ranges::lazy_split_view<adl::View<true>, adl::View<true>>; 143 using NoexceptValueType = std::ranges::iterator_t<NoexceptSplitView>::value_type; 144 using NoexceptIter = std::ranges::iterator_t<NoexceptValueType>; 145 ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval<adl::Iterator<true>>())); 146 ASSERT_NOEXCEPT(iter_move(std::declval<NoexceptIter>())); 147 } 148 } 149 150 return true; 151 } 152 153 int main(int, char**) { 154 test(); 155 static_assert(test()); 156 157 return 0; 158 } 159