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 #include "MoveOnly.h" 13 #include "test_iterators.h" 14 15 #include <cassert> 16 17 constexpr void testProxy() { 18 // constructor value 19 { 20 Proxy<int> p{5}; 21 assert(p.data == 5); 22 } 23 24 // constructor reference 25 { 26 int i = 5; 27 Proxy<int&> p{i}; 28 assert(&p.data == &i); 29 } 30 31 // constructor conversion 32 { 33 int i = 5; 34 Proxy<int&> p1{i}; 35 Proxy<int> p2 = p1; 36 assert(p2.data == 5); 37 38 Proxy<int&> p3{p2}; 39 assert(&(p3.data) == &(p2.data)); 40 41 MoveOnly m1{8}; 42 Proxy<MoveOnly&&> p4 = std::move(m1); 43 44 Proxy<MoveOnly> p5 = std::move(p4); 45 assert(p5.data.get() == 8); 46 } 47 48 // assignment 49 { 50 Proxy<int> p1{5}; 51 Proxy<int> p2{6}; 52 p1 = p2; 53 assert(p1.data == 6); 54 55 MoveOnly m1{8}; 56 Proxy<MoveOnly&&> p3 = std::move(m1); 57 Proxy<MoveOnly> p4{MoveOnly{9}}; 58 p4 = std::move(p3); 59 assert(p4.data.get() == 8); 60 61 int i = 5, j = 6; 62 Proxy<int&> p5{i}; 63 p5 = Proxy<int&>{j}; 64 assert(p5.data == 6); 65 } 66 67 // const assignment 68 { 69 int i = 5; 70 int j = 6; 71 const Proxy<int&> p1{i}; 72 const Proxy<int&> p2{j}; 73 p1 = p2; 74 assert(i == 6); 75 76 MoveOnly m1{8}; 77 MoveOnly m2{9}; 78 Proxy<MoveOnly&&> p3 = std::move(m1); 79 const Proxy<MoveOnly&&> p4 = std::move(m2); 80 p4 = std::move(p3); 81 assert(p4.data.get() == 8); 82 } 83 84 // compare 85 { 86 Proxy<int> p1{5}; 87 Proxy<int> p2{6}; 88 assert(p1 != p2); 89 assert(p1 < p2); 90 } 91 } 92 93 static_assert(std::input_iterator<ProxyIterator<cpp20_input_iterator<int*>>>); 94 static_assert(!std::forward_iterator<ProxyIterator<cpp20_input_iterator<int*>>>); 95 96 static_assert(std::forward_iterator<ProxyIterator<forward_iterator<int*>>>); 97 static_assert(!std::bidirectional_iterator<ProxyIterator<forward_iterator<int*>>>); 98 99 static_assert(std::bidirectional_iterator<ProxyIterator<bidirectional_iterator<int*>>>); 100 static_assert(!std::random_access_iterator<ProxyIterator<bidirectional_iterator<int*>>>); 101 102 static_assert(std::random_access_iterator<ProxyIterator<random_access_iterator<int*>>>); 103 static_assert(!std::contiguous_iterator<ProxyIterator<random_access_iterator<int*>>>); 104 105 static_assert(std::random_access_iterator<ProxyIterator<contiguous_iterator<int*>>>); 106 static_assert(!std::contiguous_iterator<ProxyIterator<contiguous_iterator<int*>>>); 107 108 template <class Iter> 109 constexpr void testInputIteratorOperation() { 110 int data[] = {1, 2}; 111 ProxyIterator<Iter> iter{Iter{data}}; 112 sentinel_wrapper<ProxyIterator<Iter>> sent{ProxyIterator<Iter>{Iter{data + 2}}}; 113 114 std::same_as<Proxy<int&>> decltype(auto) result = *iter; 115 assert(result.data == 1); 116 auto& iter2 = ++iter; 117 static_assert(std::is_same_v<decltype(++iter), ProxyIterator<Iter>&>); 118 assert(&iter2 == &iter); 119 assert((*iter).data == 2); 120 ++iter; 121 assert(iter == sent); 122 } 123 124 template <class Iter> 125 constexpr void testForwardIteratorOperation() { 126 int data[] = {1, 2}; 127 ProxyIterator<Iter> iter{Iter{data}}; 128 129 std::same_as<ProxyIterator<Iter>> decltype(auto) it2 = iter++; 130 assert((*it2).data == 1); 131 assert((*iter).data == 2); 132 } 133 134 template <class Iter> 135 constexpr void testBidirectionalIteratorOperation() { 136 int data[] = {1, 2}; 137 ProxyIterator<Iter> iter{Iter{data}}; 138 ++iter; 139 assert((*iter).data == 2); 140 141 auto& iter2 = --iter; 142 static_assert(std::is_same_v<decltype(--iter), ProxyIterator<Iter>&>); 143 assert(&iter2 == &iter); 144 assert((*iter).data == 1); 145 ++iter; 146 147 std::same_as<ProxyIterator<Iter>> decltype(auto) iter3 = iter--; 148 assert((*iter).data == 1); 149 assert((*iter3).data == 2); 150 } 151 152 template <class Iter> 153 constexpr void testRandomAccessIteratorOperation() { 154 int data[] = {1, 2, 3, 4, 5}; 155 ProxyIterator<Iter> iter{Iter{data}}; 156 157 auto& iter2 = iter += 2; 158 static_assert(std::is_same_v<decltype(iter += 2), ProxyIterator<Iter>&>); 159 assert(&iter2 == &iter); 160 assert((*iter).data == 3); 161 162 auto& iter3 = iter -= 1; 163 static_assert(std::is_same_v<decltype(iter -= 1), ProxyIterator<Iter>&>); 164 assert(&iter3 == &iter); 165 assert((*iter).data == 2); 166 167 std::same_as<Proxy<int&>> decltype(auto) r = iter[2]; 168 assert(r.data == 4); 169 170 std::same_as<ProxyIterator<Iter>> decltype(auto) iter4 = iter - 1; 171 assert((*iter4).data == 1); 172 173 std::same_as<ProxyIterator<Iter>> decltype(auto) iter5 = iter4 + 2; 174 assert((*iter5).data == 3); 175 176 std::same_as<ProxyIterator<Iter>> decltype(auto) iter6 = 3 + iter4; 177 assert((*iter6).data == 4); 178 179 std::same_as<std::iter_difference_t<Iter>> decltype(auto) n = iter6 - iter5; 180 assert(n == 1); 181 182 assert(iter4 < iter5); 183 assert(iter3 <= iter5); 184 assert(iter5 > iter4); 185 assert(iter6 >= iter4); 186 } 187 188 constexpr void testProxyIterator() { 189 // input iterator operations 190 { 191 testInputIteratorOperation<cpp20_input_iterator<int*>>(); 192 testInputIteratorOperation<forward_iterator<int*>>(); 193 testInputIteratorOperation<bidirectional_iterator<int*>>(); 194 testInputIteratorOperation<random_access_iterator<int*>>(); 195 testInputIteratorOperation<contiguous_iterator<int*>>(); 196 } 197 198 // forward iterator operations 199 { 200 testForwardIteratorOperation<forward_iterator<int*>>(); 201 testForwardIteratorOperation<bidirectional_iterator<int*>>(); 202 testForwardIteratorOperation<random_access_iterator<int*>>(); 203 testForwardIteratorOperation<contiguous_iterator<int*>>(); 204 } 205 206 // bidirectional iterator operations 207 { 208 testBidirectionalIteratorOperation<bidirectional_iterator<int*>>(); 209 testBidirectionalIteratorOperation<random_access_iterator<int*>>(); 210 testBidirectionalIteratorOperation<contiguous_iterator<int*>>(); 211 } 212 213 // random access iterator operations 214 { 215 testRandomAccessIteratorOperation<random_access_iterator<int*>>(); 216 testRandomAccessIteratorOperation<contiguous_iterator<int*>>(); 217 } 218 } 219 220 constexpr void testProxyRange() { 221 int data[] = {3, 4, 5}; 222 ProxyRange r{data}; 223 std::same_as<ProxyIterator<int*>> decltype(auto) it = std::ranges::begin(r); 224 assert((*it).data == 3); 225 it += 3; 226 assert(it == std::ranges::end(r)); 227 } 228 229 template <class Iter> 230 concept StdMoveWorks = requires(std::iter_value_t<Iter> val, Iter iter) { val = std::move(*iter); }; 231 232 static_assert(StdMoveWorks<MoveOnly*>); 233 static_assert(!StdMoveWorks<ProxyIterator<MoveOnly*>>); 234 235 // although this "works" but it actually creates a copy instead of move 236 static_assert(StdMoveWorks<ProxyIterator<int*>>); 237 238 using std::swap; 239 240 template <class Iter> 241 concept SwapWorks = requires(Iter iter1, Iter iter2) { swap(*iter1, *iter2); }; 242 243 static_assert(SwapWorks<int*>); 244 static_assert(!SwapWorks<ProxyIterator<int*>>); 245 246 constexpr bool test() { 247 testProxy(); 248 testProxyIterator(); 249 testProxyRange(); 250 251 // iter_move 252 { 253 MoveOnly data[] = {5, 6, 7}; 254 ProxyRange r{data}; 255 auto it = r.begin(); 256 std::iter_value_t<decltype(it)> moved = std::ranges::iter_move(it); 257 assert(moved.data.get() == 5); 258 } 259 260 // iter_swap 261 { 262 MoveOnly data[] = {5, 6, 7}; 263 ProxyRange r{data}; 264 auto it1 = r.begin(); 265 auto it2 = it1 + 2; 266 std::ranges::iter_swap(it1, it2); 267 assert(data[0].get() == 7); 268 assert(data[2].get() == 5); 269 } 270 271 return true; 272 } 273 274 int main(int, const char**) { 275 test(); 276 static_assert(test()); 277 278 return 0; 279 } 280