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