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 | |
11 | // <memory> |
12 | // |
13 | // template<input_iterator InputIterator, nothrow-forward-iterator OutputIterator, nothrow-sentinel-for<OutputIterator> Sentinel> |
14 | // requires constructible_from<iter_value_t<OutputIterator>, iter_rvalue_reference_t<InputIterator>> |
15 | // ranges::uninitialized_move_n_result<InputIterator, OutputIterator> |
16 | // ranges::uninitialized_move_n(InputIterator ifirst, iter_difference_t<InputIterator> n, OutputIterator ofirst, Sentinel olast); // since C++20 |
17 | |
18 | |
19 | #include <algorithm> |
20 | #include <array> |
21 | #include <cassert> |
22 | #include <iterator> |
23 | #include <memory> |
24 | #include <ranges> |
25 | #include <type_traits> |
26 | #include <utility> |
27 | |
28 | #include "../buffer.h" |
29 | #include "../counted.h" |
30 | #include "../overload_compare_iterator.h" |
31 | #include "MoveOnly.h" |
32 | #include "test_macros.h" |
33 | #include "test_iterators.h" |
34 | |
35 | // TODO(varconst): consolidate the ADL checks into a single file. |
36 | // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, |
37 | // implementations are allowed to use a different mechanism to achieve this effect, so this check is |
38 | // libc++-specific. |
39 | LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_move)>); |
40 | |
41 | static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_move), int*, int*, long*, long*>); |
42 | struct NotConvertibleFromInt {}; |
43 | static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_move), int*, int*, NotConvertibleFromInt*, |
44 | NotConvertibleFromInt*>); |
45 | |
46 | int main(int, char**) { |
47 | // An empty range -- no default constructors should be invoked. |
48 | { |
49 | Counted in[] = {Counted()}; |
50 | Buffer<Counted, 1> out; |
51 | Counted::reset(); |
52 | |
53 | { |
54 | auto result = std::ranges::uninitialized_move(in, in, out.begin(), out.end()); |
55 | assert(Counted::current_objects == 0); |
56 | assert(Counted::total_objects == 0); |
57 | assert(Counted::total_copies == 0); |
58 | assert(result.in == in); |
59 | assert(result.out == out.begin()); |
60 | } |
61 | |
62 | { |
63 | std::ranges::empty_view<Counted> view; |
64 | auto result = std::ranges::uninitialized_move(view, out); |
65 | assert(Counted::current_objects == 0); |
66 | assert(Counted::total_objects == 0); |
67 | assert(Counted::total_copies == 0); |
68 | assert(result.in == view.begin()); |
69 | assert(result.out == out.begin()); |
70 | } |
71 | |
72 | { |
73 | forward_iterator<Counted*> it(in); |
74 | std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(it)); |
75 | |
76 | auto result = std::ranges::uninitialized_move(range.begin(), range.end(), out.begin(), out.end()); |
77 | assert(Counted::current_objects == 0); |
78 | assert(Counted::total_objects == 0); |
79 | assert(Counted::total_copies == 0); |
80 | assert(result.in == it); |
81 | assert(result.out == out.begin()); |
82 | } |
83 | |
84 | { |
85 | forward_iterator<Counted*> it(in); |
86 | std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(it)); |
87 | |
88 | auto result = std::ranges::uninitialized_move(range, out); |
89 | assert(Counted::current_objects == 0); |
90 | assert(Counted::total_objects == 0); |
91 | assert(Counted::total_copies == 0); |
92 | assert(result.in == it); |
93 | assert(result.out == out.begin()); |
94 | } |
95 | Counted::reset(); |
96 | } |
97 | |
98 | // A range containing several objects, (iter, sentinel) overload. |
99 | { |
100 | constexpr int N = 5; |
101 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
102 | Buffer<Counted, N> out; |
103 | Counted::reset(); |
104 | |
105 | auto result = std::ranges::uninitialized_move(in, in + N, out.begin(), out.end()); |
106 | ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_move_result<Counted*, Counted*>); |
107 | assert(Counted::current_objects == N); |
108 | assert(Counted::total_objects == N); |
109 | assert(Counted::total_moves == N); |
110 | assert(Counted::total_copies == 0); |
111 | |
112 | assert(std::equal(in, in + N, out.begin(), out.end())); |
113 | assert(result.in == in + N); |
114 | assert(result.out == out.end()); |
115 | |
116 | std::destroy(first: out.begin(), last: out.end()); |
117 | } |
118 | Counted::reset(); |
119 | |
120 | // A range containing several objects, (range) overload. |
121 | { |
122 | constexpr int N = 5; |
123 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
124 | Buffer<Counted, N> out; |
125 | Counted::reset(); |
126 | |
127 | std::ranges::subrange range(in, in + N); |
128 | auto result = std::ranges::uninitialized_move(range, out); |
129 | ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_move_result<Counted*, Counted*>); |
130 | |
131 | assert(Counted::current_objects == N); |
132 | assert(Counted::total_objects == N); |
133 | assert(Counted::total_moves == N); |
134 | assert(Counted::total_copies == 0); |
135 | assert(std::equal(in, in + N, out.begin(), out.end())); |
136 | |
137 | assert(result.in == in + N); |
138 | assert(result.out == out.end()); |
139 | |
140 | std::destroy(first: out.begin(), last: out.end()); |
141 | } |
142 | Counted::reset(); |
143 | |
144 | // Using `counted_iterator`. |
145 | { |
146 | constexpr int N = 3; |
147 | Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
148 | Buffer<Counted, 5> out; |
149 | Counted::reset(); |
150 | |
151 | std::counted_iterator iter(in, N); |
152 | auto result = std::ranges::uninitialized_move(iter, std::default_sentinel, out.begin(), out.end()); |
153 | |
154 | assert(Counted::current_objects == N); |
155 | assert(Counted::total_objects == N); |
156 | assert(Counted::total_moves == N); |
157 | assert(Counted::total_copies == 0); |
158 | assert(std::equal(in, in + N, out.begin(), out.begin() + N)); |
159 | |
160 | assert(result.in == iter + N); |
161 | assert(result.out == out.begin() + N); |
162 | |
163 | std::destroy(first: out.begin(), last: out.begin() + N); |
164 | } |
165 | Counted::reset(); |
166 | |
167 | // Using `views::counted`. |
168 | { |
169 | constexpr int N = 3; |
170 | Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
171 | Buffer<Counted, 5> out; |
172 | Counted::reset(); |
173 | |
174 | auto view = std::views::counted(in, N); |
175 | auto result = std::ranges::uninitialized_move(view, out); |
176 | |
177 | assert(Counted::current_objects == N); |
178 | assert(Counted::total_objects == N); |
179 | assert(Counted::total_moves == N); |
180 | assert(Counted::total_copies == 0); |
181 | assert(std::equal(in, in + N, out.begin(), out.begin() + N)); |
182 | |
183 | assert(result.in == view.begin() + N); |
184 | assert(result.out == out.begin() + N); |
185 | |
186 | std::destroy(first: out.begin(), last: out.begin() + N); |
187 | } |
188 | Counted::reset(); |
189 | |
190 | // Using `reverse_view`. |
191 | { |
192 | constexpr int N = 3; |
193 | Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
194 | Buffer<Counted, 5> out; |
195 | Counted::reset(); |
196 | |
197 | std::ranges::subrange range(in, in + N); |
198 | auto view = std::ranges::views::reverse(range); |
199 | auto result = std::ranges::uninitialized_move(view, out); |
200 | |
201 | assert(Counted::current_objects == N); |
202 | assert(Counted::total_objects == N); |
203 | assert(Counted::total_moves == N); |
204 | assert(Counted::total_copies == 0); |
205 | |
206 | Counted expected[N] = {Counted(3), Counted(2), Counted(1)}; |
207 | assert(std::equal(out.begin(), out.begin() + N, expected, expected + N)); |
208 | |
209 | assert(result.in == view.begin() + N); |
210 | assert(result.out == out.begin() + N); |
211 | |
212 | std::destroy(first: out.begin(), last: out.begin() + N); |
213 | } |
214 | Counted::reset(); |
215 | |
216 | // Any existing values should be overwritten by move constructors. |
217 | { |
218 | constexpr int N = 5; |
219 | int in[N] = {1, 2, 3, 4, 5}; |
220 | int out[N] = {6, 7, 8, 9, 10}; |
221 | assert(!std::equal(in, in + N, out, out + N)); |
222 | |
223 | std::ranges::uninitialized_move(in, in + 1, out, out + N); |
224 | assert(out[0] == 1); |
225 | assert(out[1] == 7); |
226 | |
227 | std::ranges::uninitialized_move(in, in + N, out, out + N); |
228 | assert(std::equal(in, in + N, out, out + N)); |
229 | } |
230 | |
231 | // An exception is thrown while objects are being created -- check that the objects in the source |
232 | // range have been moved from. (iterator, sentinel) overload. |
233 | #ifndef TEST_HAS_NO_EXCEPTIONS |
234 | { |
235 | constexpr int N = 3; |
236 | Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
237 | Buffer<Counted, 5> out; |
238 | Counted::reset(); |
239 | |
240 | Counted::throw_on = N; // When constructing out[3]. |
241 | try { |
242 | std::ranges::uninitialized_move(in, in + 5, out.begin(), out.end()); |
243 | assert(false); |
244 | } catch (...) { |
245 | } |
246 | assert(Counted::current_objects == 0); |
247 | assert(Counted::total_objects == N); |
248 | assert(Counted::total_moves == N); |
249 | assert(Counted::total_copies == 0); |
250 | |
251 | assert(std::all_of(in, in + 1, [](const auto& e) { return e.moved_from; })); |
252 | assert(std::none_of(in + N, in + 5, [](const auto& e) { return e.moved_from; })); |
253 | |
254 | std::destroy(first: out.begin(), last: out.begin() + N); |
255 | } |
256 | Counted::reset(); |
257 | |
258 | // An exception is thrown while objects are being created -- check that the objects in the source |
259 | // range have been moved from. (range) overload. |
260 | { |
261 | constexpr int N = 3; |
262 | Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
263 | Buffer<Counted, 5> out; |
264 | Counted::reset(); |
265 | |
266 | Counted::throw_on = N; // When constructing out[3]. |
267 | try { |
268 | std::ranges::uninitialized_move(in, out); |
269 | assert(false); |
270 | } catch (...) { |
271 | } |
272 | assert(Counted::current_objects == 0); |
273 | assert(Counted::total_objects == N); |
274 | assert(Counted::total_moves == N); |
275 | assert(Counted::total_copies == 0); |
276 | |
277 | assert(std::all_of(in, in + 1, [](const auto& e) { return e.moved_from; })); |
278 | assert(std::none_of(in + N, in + 5, [](const auto& e) { return e.moved_from; })); |
279 | |
280 | std::destroy(first: out.begin(), last: out.begin() + N); |
281 | } |
282 | Counted::reset(); |
283 | #endif // TEST_HAS_NO_EXCEPTIONS |
284 | |
285 | // Works with const iterators, (iter, sentinel) overload. |
286 | { |
287 | constexpr int N = 5; |
288 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
289 | Buffer<Counted, N> out; |
290 | Counted::reset(); |
291 | |
292 | std::ranges::uninitialized_move(in, in + N, out.cbegin(), out.cend()); |
293 | assert(Counted::current_objects == N); |
294 | assert(Counted::total_objects == N); |
295 | assert(std::equal(in, in + N, out.begin(), out.end())); |
296 | |
297 | std::destroy(first: out.begin(), last: out.end()); |
298 | } |
299 | Counted::reset(); |
300 | |
301 | // Works with const iterators, (range) overload. |
302 | { |
303 | constexpr int N = 5; |
304 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
305 | Buffer<Counted, N> out; |
306 | Counted::reset(); |
307 | |
308 | std::ranges::subrange out_range (out.cbegin(), out.cend()); |
309 | std::ranges::uninitialized_move(in, out_range); |
310 | assert(Counted::current_objects == N); |
311 | assert(Counted::total_objects == N); |
312 | assert(std::equal(in, in + N, out.begin(), out.end())); |
313 | |
314 | std::destroy(first: out.begin(), last: out.end()); |
315 | } |
316 | Counted::reset(); |
317 | |
318 | // Conversions, (iter, sentinel) overload. |
319 | { |
320 | constexpr int N = 3; |
321 | int in[N] = {1, 2, 3}; |
322 | Buffer<double, N> out; |
323 | |
324 | std::ranges::uninitialized_move(in, in + N, out.begin(), out.end()); |
325 | assert(std::equal(in, in + N, out.begin(), out.end())); |
326 | } |
327 | |
328 | // Conversions, (range) overload. |
329 | { |
330 | constexpr int N = 3; |
331 | int in[N] = {1, 2, 3}; |
332 | Buffer<double, N> out; |
333 | |
334 | std::ranges::uninitialized_move(in, out); |
335 | assert(std::equal(in, in + N, out.begin(), out.end())); |
336 | } |
337 | |
338 | // Destination range is shorter than the source range, (iter, sentinel) overload. |
339 | { |
340 | constexpr int M = 3; |
341 | constexpr int N = 5; |
342 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
343 | Buffer<Counted, M> out; |
344 | Counted::reset(); |
345 | |
346 | auto result = std::ranges::uninitialized_move(in, in + N, out.begin(), out.end()); |
347 | assert(Counted::current_objects == M); |
348 | assert(Counted::total_objects == M); |
349 | assert(Counted::total_moves == M); |
350 | assert(Counted::total_copies == 0); |
351 | |
352 | assert(std::equal(in, in + M, out.begin(), out.end())); |
353 | assert(result.in == in + M); |
354 | assert(result.out == out.end()); |
355 | } |
356 | |
357 | // Destination range is shorter than the source range, (range) overload. |
358 | { |
359 | constexpr int M = 3; |
360 | constexpr int N = 5; |
361 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
362 | Buffer<Counted, M> out; |
363 | Counted::reset(); |
364 | |
365 | std::ranges::subrange range(in, in + N); |
366 | auto result = std::ranges::uninitialized_move(range, out); |
367 | assert(Counted::current_objects == M); |
368 | assert(Counted::total_objects == M); |
369 | assert(Counted::total_moves == M); |
370 | assert(Counted::total_copies == 0); |
371 | |
372 | assert(std::equal(in, in + M, out.begin(), out.end())); |
373 | assert(result.in == in + M); |
374 | assert(result.out == out.end()); |
375 | } |
376 | |
377 | // Ensure the `iter_move` customization point is being used. |
378 | { |
379 | constexpr int N = 3; |
380 | int in[N] = {1, 2, 3}; |
381 | Buffer<int, N> out; |
382 | int iter_moves = 0; |
383 | adl::Iterator begin = adl::Iterator::TrackMoves(in, iter_moves); |
384 | adl::Iterator end = adl::Iterator::TrackMoves(in + N, iter_moves); |
385 | |
386 | std::ranges::uninitialized_move(begin, end, out.begin(), out.end()); |
387 | assert(iter_moves == 3); |
388 | iter_moves = 0; |
389 | |
390 | std::ranges::subrange range(begin, end); |
391 | std::ranges::uninitialized_move(range, out); |
392 | assert(iter_moves == 3); |
393 | iter_moves = 0; |
394 | } |
395 | |
396 | // Move-only iterators are supported. |
397 | { |
398 | using MoveOnlyIter = cpp20_input_iterator<const int*>; |
399 | static_assert(!std::is_copy_constructible_v<MoveOnlyIter>); |
400 | |
401 | constexpr int N = 3; |
402 | struct MoveOnlyRange { |
403 | int buffer[N] = {1, 2, 3}; |
404 | auto begin() const { return MoveOnlyIter(buffer); } |
405 | auto end() const { return sentinel_wrapper<MoveOnlyIter>(MoveOnlyIter(buffer)); } |
406 | }; |
407 | static_assert(std::ranges::input_range<MoveOnlyRange>); |
408 | MoveOnlyRange in; |
409 | |
410 | // (iter, sentinel) overload. |
411 | { |
412 | Buffer<int, N> out; |
413 | std::ranges::uninitialized_move(in.begin(), in.end(), out.begin(), out.end()); |
414 | } |
415 | |
416 | // (range) overload. |
417 | { |
418 | Buffer<int, N> out; |
419 | std::ranges::uninitialized_move(in, out); |
420 | } |
421 | } |
422 | |
423 | // MoveOnly types are supported |
424 | { |
425 | { |
426 | MoveOnly a[] = {1, 2, 3, 4}; |
427 | Buffer<MoveOnly, 4> out; |
428 | std::ranges::uninitialized_move(std::begin(a), std::end(a), std::begin(out), std::end(out)); |
429 | assert(std::ranges::equal(out, std::array<MoveOnly, 4>{1, 2, 3, 4})); |
430 | } |
431 | { |
432 | MoveOnly a[] = {1, 2, 3, 4}; |
433 | Buffer<MoveOnly, 4> out; |
434 | std::ranges::uninitialized_move(a, out); |
435 | assert(std::ranges::equal(out, std::array<MoveOnly, 4>{1, 2, 3, 4})); |
436 | } |
437 | } |
438 | |
439 | // Test with an iterator that overloads operator== and operator!= as the input and output iterators |
440 | { |
441 | using T = int; |
442 | using Iterator = overload_compare_iterator<T*>; |
443 | const int N = 5; |
444 | |
445 | // input |
446 | { |
447 | char pool[sizeof(T) * N] = {0}; |
448 | T* p = reinterpret_cast<T*>(pool); |
449 | T* p_end = reinterpret_cast<T*>(pool) + N; |
450 | T array[N] = {1, 2, 3, 4, 5}; |
451 | std::ranges::uninitialized_move(Iterator(array), Iterator(array + N), p, p_end); |
452 | for (int i = 0; i != N; ++i) { |
453 | assert(array[i] == p[i]); |
454 | } |
455 | } |
456 | |
457 | // output |
458 | { |
459 | char pool[sizeof(T) * N] = {0}; |
460 | T* p = reinterpret_cast<T*>(pool); |
461 | T* p_end = reinterpret_cast<T*>(pool) + N; |
462 | T array[N] = {1, 2, 3, 4, 5}; |
463 | std::ranges::uninitialized_move(array, array + N, Iterator(p), Iterator(p_end)); |
464 | for (int i = 0; i != N; ++i) { |
465 | assert(array[i] == p[i]); |
466 | } |
467 | } |
468 | } |
469 | |
470 | return 0; |
471 | } |
472 | |