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.
39LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_move)>);
40
41static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_move), int*, int*, long*, long*>);
42struct NotConvertibleFromInt {};
43static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_move), int*, int*, NotConvertibleFromInt*,
44 NotConvertibleFromInt*>);
45
46int 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 // Conversions, (iter, sentinel) overload.
286 {
287 constexpr int N = 3;
288 int in[N] = {1, 2, 3};
289 Buffer<double, N> out;
290
291 std::ranges::uninitialized_move(in, in + N, out.begin(), out.end());
292 assert(std::equal(in, in + N, out.begin(), out.end()));
293 }
294
295 // Conversions, (range) overload.
296 {
297 constexpr int N = 3;
298 int in[N] = {1, 2, 3};
299 Buffer<double, N> out;
300
301 std::ranges::uninitialized_move(in, out);
302 assert(std::equal(in, in + N, out.begin(), out.end()));
303 }
304
305 // Destination range is shorter than the source range, (iter, sentinel) overload.
306 {
307 constexpr int M = 3;
308 constexpr int N = 5;
309 Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
310 Buffer<Counted, M> out;
311 Counted::reset();
312
313 auto result = std::ranges::uninitialized_move(in, in + N, out.begin(), out.end());
314 assert(Counted::current_objects == M);
315 assert(Counted::total_objects == M);
316 assert(Counted::total_moves == M);
317 assert(Counted::total_copies == 0);
318
319 assert(std::equal(in, in + M, out.begin(), out.end()));
320 assert(result.in == in + M);
321 assert(result.out == out.end());
322 }
323
324 // Destination range is shorter than the source range, (range) overload.
325 {
326 constexpr int M = 3;
327 constexpr int N = 5;
328 Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
329 Buffer<Counted, M> out;
330 Counted::reset();
331
332 std::ranges::subrange range(in, in + N);
333 auto result = std::ranges::uninitialized_move(range, out);
334 assert(Counted::current_objects == M);
335 assert(Counted::total_objects == M);
336 assert(Counted::total_moves == M);
337 assert(Counted::total_copies == 0);
338
339 assert(std::equal(in, in + M, out.begin(), out.end()));
340 assert(result.in == in + M);
341 assert(result.out == out.end());
342 }
343
344 // Ensure the `iter_move` customization point is being used.
345 {
346 constexpr int N = 3;
347 int in[N] = {1, 2, 3};
348 Buffer<int, N> out;
349 int iter_moves = 0;
350 adl::Iterator begin = adl::Iterator::TrackMoves(in, iter_moves);
351 adl::Iterator end = adl::Iterator::TrackMoves(in + N, iter_moves);
352
353 std::ranges::uninitialized_move(begin, end, out.begin(), out.end());
354 assert(iter_moves == 3);
355 iter_moves = 0;
356
357 std::ranges::subrange range(begin, end);
358 std::ranges::uninitialized_move(range, out);
359 assert(iter_moves == 3);
360 iter_moves = 0;
361 }
362
363 // Move-only iterators are supported.
364 {
365 using MoveOnlyIter = cpp20_input_iterator<const int*>;
366 static_assert(!std::is_copy_constructible_v<MoveOnlyIter>);
367
368 constexpr int N = 3;
369 struct MoveOnlyRange {
370 int buffer[N] = {1, 2, 3};
371 auto begin() const { return MoveOnlyIter(buffer); }
372 auto end() const { return sentinel_wrapper<MoveOnlyIter>(MoveOnlyIter(buffer)); }
373 };
374 static_assert(std::ranges::input_range<MoveOnlyRange>);
375 MoveOnlyRange in;
376
377 // (iter, sentinel) overload.
378 {
379 Buffer<int, N> out;
380 std::ranges::uninitialized_move(in.begin(), in.end(), out.begin(), out.end());
381 }
382
383 // (range) overload.
384 {
385 Buffer<int, N> out;
386 std::ranges::uninitialized_move(in, out);
387 }
388 }
389
390 // MoveOnly types are supported
391 {
392 {
393 MoveOnly a[] = {1, 2, 3, 4};
394 Buffer<MoveOnly, 4> out;
395 std::ranges::uninitialized_move(std::begin(a), std::end(a), std::begin(out), std::end(out));
396 assert(std::ranges::equal(out, std::array<MoveOnly, 4>{1, 2, 3, 4}));
397 }
398 {
399 MoveOnly a[] = {1, 2, 3, 4};
400 Buffer<MoveOnly, 4> out;
401 std::ranges::uninitialized_move(a, out);
402 assert(std::ranges::equal(out, std::array<MoveOnly, 4>{1, 2, 3, 4}));
403 }
404 }
405
406 // Test with an iterator that overloads operator== and operator!= as the input and output iterators
407 {
408 using T = int;
409 using Iterator = overload_compare_iterator<T*>;
410 const int N = 5;
411
412 // input
413 {
414 char pool[sizeof(T) * N] = {0};
415 T* p = reinterpret_cast<T*>(pool);
416 T* p_end = reinterpret_cast<T*>(pool) + N;
417 T array[N] = {1, 2, 3, 4, 5};
418 std::ranges::uninitialized_move(Iterator(array), Iterator(array + N), p, p_end);
419 for (int i = 0; i != N; ++i) {
420 assert(array[i] == p[i]);
421 }
422 }
423
424 // output
425 {
426 char pool[sizeof(T) * N] = {0};
427 T* p = reinterpret_cast<T*>(pool);
428 T* p_end = reinterpret_cast<T*>(pool) + N;
429 T array[N] = {1, 2, 3, 4, 5};
430 std::ranges::uninitialized_move(array, array + N, Iterator(p), Iterator(p_end));
431 for (int i = 0; i != N; ++i) {
432 assert(array[i] == p[i]);
433 }
434 }
435 }
436
437 return 0;
438}
439

source code of libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp