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