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 I, sentinel-for<I> S1, nothrow-forward-iterator O, nothrow-sentinel-for<O> S2> |
14 | // requires constructible_from<iter_value_t<O>, iter_reference_t<I>> |
15 | // uninitialized_copy_result<I, O> ranges::uninitialized_copy(I ifirst, S1 ilast, O ofirst, S2 olast); // since C++20 |
16 | // |
17 | // template<input_range IR, nothrow-forward-range OR> |
18 | // requires constructible_from<range_value_t<OR>, range_reference_t<IR>> |
19 | // uninitialized_copy_result<borrowed_iterator_t<IR>, borrowed_iterator_t<OR>> ranges::uninitialized_copy(IR&& in_range, OR&& out_range); // since C++20 |
20 | |
21 | #include <algorithm> |
22 | #include <cassert> |
23 | #include <iterator> |
24 | #include <memory> |
25 | #include <ranges> |
26 | #include <type_traits> |
27 | |
28 | #include "../buffer.h" |
29 | #include "../counted.h" |
30 | #include "../overload_compare_iterator.h" |
31 | #include "test_macros.h" |
32 | #include "test_iterators.h" |
33 | |
34 | // TODO(varconst): consolidate the ADL checks into a single file. |
35 | // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However, |
36 | // implementations are allowed to use a different mechanism to achieve this effect, so this check is |
37 | // libc++-specific. |
38 | LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_copy)>); |
39 | |
40 | static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_copy), int*, int*, long*, long*>); |
41 | struct NotConvertibleFromInt {}; |
42 | static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_copy), int*, int*, NotConvertibleFromInt*, |
43 | NotConvertibleFromInt*>); |
44 | |
45 | int main(int, char**) { |
46 | // An empty range -- no default constructors should be invoked. |
47 | { |
48 | Counted in[] = {Counted()}; |
49 | Buffer<Counted, 1> out; |
50 | Counted::reset(); |
51 | |
52 | { |
53 | auto result = std::ranges::uninitialized_copy(in, in, out.begin(), out.end()); |
54 | assert(Counted::current_objects == 0); |
55 | assert(Counted::total_objects == 0); |
56 | assert(Counted::total_copies == 0); |
57 | assert(result.in == in); |
58 | assert(result.out == out.begin()); |
59 | } |
60 | |
61 | { |
62 | std::ranges::empty_view<Counted> view; |
63 | auto result = std::ranges::uninitialized_copy(view, out); |
64 | assert(Counted::current_objects == 0); |
65 | assert(Counted::total_objects == 0); |
66 | assert(Counted::total_copies == 0); |
67 | assert(result.in == view.begin()); |
68 | assert(result.out == out.begin()); |
69 | } |
70 | |
71 | { |
72 | forward_iterator<Counted*> it(in); |
73 | std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(it)); |
74 | |
75 | auto result = std::ranges::uninitialized_copy(range.begin(), range.end(), out.begin(), out.end()); |
76 | assert(Counted::current_objects == 0); |
77 | assert(Counted::total_objects == 0); |
78 | assert(Counted::total_copies == 0); |
79 | assert(result.in == it); |
80 | assert(result.out == out.begin()); |
81 | } |
82 | |
83 | { |
84 | forward_iterator<Counted*> it(in); |
85 | std::ranges::subrange range(it, sentinel_wrapper<forward_iterator<Counted*>>(it)); |
86 | |
87 | auto result = std::ranges::uninitialized_copy(range, out); |
88 | assert(Counted::current_objects == 0); |
89 | assert(Counted::total_objects == 0); |
90 | assert(Counted::total_copies == 0); |
91 | assert(result.in == it); |
92 | assert(result.out == out.begin()); |
93 | } |
94 | Counted::reset(); |
95 | } |
96 | |
97 | // A range containing several objects, (iter, sentinel) overload. |
98 | { |
99 | constexpr int N = 5; |
100 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
101 | Buffer<Counted, N> out; |
102 | Counted::reset(); |
103 | |
104 | auto result = std::ranges::uninitialized_copy(in, in + N, out.begin(), out.end()); |
105 | ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_copy_result<Counted*, Counted*>); |
106 | |
107 | assert(Counted::current_objects == N); |
108 | assert(Counted::total_objects == N); |
109 | assert(Counted::total_copies == N); |
110 | assert(Counted::total_moves == 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_copy(range, out); |
129 | ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_copy_result<Counted*, Counted*>); |
130 | |
131 | assert(Counted::current_objects == N); |
132 | assert(Counted::total_objects == N); |
133 | assert(Counted::total_copies == N); |
134 | assert(Counted::total_moves == 0); |
135 | |
136 | assert(std::equal(in, in + N, out.begin(), out.end())); |
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_copy(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_copies == N); |
157 | assert(Counted::total_moves == 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_copy(view, out); |
176 | |
177 | assert(Counted::current_objects == N); |
178 | assert(Counted::total_objects == N); |
179 | assert(Counted::total_copies == N); |
180 | assert(Counted::total_moves == 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_copy(view, out); |
200 | |
201 | assert(Counted::current_objects == N); |
202 | assert(Counted::total_objects == N); |
203 | assert(Counted::total_copies == N); |
204 | assert(Counted::total_moves == 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 copy 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_copy(in, in + 1, out, out + N); |
224 | assert(out[0] == 1); |
225 | assert(out[1] == 7); |
226 | |
227 | std::ranges::uninitialized_copy(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 -- objects not yet overwritten should |
232 | // stay valid. (iterator, sentinel) overload. |
233 | #ifndef TEST_HAS_NO_EXCEPTIONS |
234 | { |
235 | constexpr int M = 3; |
236 | constexpr int N = 5; |
237 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
238 | Counted out[N] = {Counted(6), Counted(7), Counted(8), Counted(9), Counted(10)}; |
239 | Counted::reset(); |
240 | |
241 | Counted::throw_on = M; // When constructing out[3]. |
242 | try { |
243 | std::ranges::uninitialized_copy(in, in + N, out, out + N); |
244 | assert(false); |
245 | } catch (...) { |
246 | } |
247 | assert(Counted::current_objects == 0); |
248 | assert(Counted::total_objects == M); |
249 | assert(Counted::total_copies == M); |
250 | assert(Counted::total_moves == 0); |
251 | |
252 | assert(out[4].value == 10); |
253 | } |
254 | Counted::reset(); |
255 | |
256 | // An exception is thrown while objects are being created -- objects not yet overwritten should |
257 | // stay valid. (range) overload. |
258 | { |
259 | constexpr int M = 3; |
260 | constexpr int N = 5; |
261 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
262 | Counted out[N] = {Counted(6), Counted(7), Counted(8), Counted(9), Counted(10)}; |
263 | Counted::reset(); |
264 | |
265 | Counted::throw_on = M; // When constructing out[3]. |
266 | try { |
267 | std::ranges::uninitialized_copy(in, out); |
268 | assert(false); |
269 | } catch (...) { |
270 | } |
271 | assert(Counted::current_objects == 0); |
272 | assert(Counted::total_objects == M); |
273 | assert(Counted::total_copies == M); |
274 | assert(Counted::total_moves == 0); |
275 | |
276 | assert(out[4].value == 10); |
277 | } |
278 | Counted::reset(); |
279 | #endif // TEST_HAS_NO_EXCEPTIONS |
280 | |
281 | // Works with const iterators, (iter, sentinel) overload. |
282 | { |
283 | constexpr int N = 5; |
284 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
285 | Buffer<Counted, N> out; |
286 | Counted::reset(); |
287 | |
288 | std::ranges::uninitialized_copy(in, in + N, out.cbegin(), out.cend()); |
289 | assert(Counted::current_objects == N); |
290 | assert(Counted::total_objects == N); |
291 | assert(std::equal(in, in + N, out.begin(), out.end())); |
292 | |
293 | std::destroy(first: out.begin(), last: out.end()); |
294 | } |
295 | Counted::reset(); |
296 | |
297 | // Works with const iterators, (range) overload. |
298 | { |
299 | constexpr int N = 5; |
300 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
301 | Buffer<Counted, N> out; |
302 | Counted::reset(); |
303 | |
304 | std::ranges::subrange out_range(out.cbegin(), out.cend()); |
305 | std::ranges::uninitialized_copy(in, out_range); |
306 | assert(Counted::current_objects == N); |
307 | assert(Counted::total_objects == N); |
308 | assert(std::equal(in, in + N, out.begin(), out.end())); |
309 | |
310 | std::destroy(first: out.begin(), last: out.end()); |
311 | } |
312 | Counted::reset(); |
313 | |
314 | // Conversions, (iter, sentinel) overload. |
315 | { |
316 | constexpr int N = 3; |
317 | int in[N] = {1, 2, 3}; |
318 | Buffer<double, N> out; |
319 | |
320 | std::ranges::uninitialized_copy(in, in + N, out.begin(), out.end()); |
321 | assert(std::equal(in, in + N, out.begin(), out.end())); |
322 | } |
323 | |
324 | // Conversions, (range) overload. |
325 | { |
326 | constexpr int N = 3; |
327 | int in[N] = {1, 2, 3}; |
328 | Buffer<double, N> out; |
329 | |
330 | std::ranges::uninitialized_copy(in, out); |
331 | assert(std::equal(in, in + N, out.begin(), out.end())); |
332 | } |
333 | |
334 | // Destination range is shorter than the source range, (iter, sentinel) overload. |
335 | { |
336 | constexpr int M = 3; |
337 | constexpr int N = 5; |
338 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
339 | Buffer<Counted, M> out; |
340 | Counted::reset(); |
341 | |
342 | auto result = std::ranges::uninitialized_copy(in, in + N, out.begin(), out.end()); |
343 | assert(Counted::current_objects == M); |
344 | assert(Counted::total_objects == M); |
345 | assert(Counted::total_copies == M); |
346 | assert(Counted::total_moves == 0); |
347 | |
348 | assert(std::equal(in, in + M, out.begin(), out.end())); |
349 | assert(result.in == in + M); |
350 | assert(result.out == out.end()); |
351 | } |
352 | |
353 | // Destination range is shorter than the source range, (range) overload. |
354 | { |
355 | constexpr int M = 3; |
356 | constexpr int N = 5; |
357 | Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)}; |
358 | Buffer<Counted, M> out; |
359 | Counted::reset(); |
360 | |
361 | std::ranges::subrange range(in, in + N); |
362 | auto result = std::ranges::uninitialized_copy(range, out); |
363 | assert(Counted::current_objects == M); |
364 | assert(Counted::total_objects == M); |
365 | assert(Counted::total_copies == M); |
366 | assert(Counted::total_moves == 0); |
367 | |
368 | assert(std::equal(in, in + M, out.begin(), out.end())); |
369 | assert(result.in == in + M); |
370 | assert(result.out == out.end()); |
371 | } |
372 | |
373 | // Move-only iterators are supported. |
374 | { |
375 | using MoveOnlyIter = cpp20_input_iterator<const int*>; |
376 | static_assert(!std::is_copy_constructible_v<MoveOnlyIter>); |
377 | |
378 | constexpr int N = 3; |
379 | struct MoveOnlyRange { |
380 | int buffer[N] = {1, 2, 3}; |
381 | auto begin() const { return MoveOnlyIter(buffer); } |
382 | auto end() const { return sentinel_wrapper<MoveOnlyIter>(MoveOnlyIter(buffer)); } |
383 | }; |
384 | static_assert(std::ranges::input_range<MoveOnlyRange>); |
385 | MoveOnlyRange in; |
386 | |
387 | // (iter, sentinel) overload. |
388 | { |
389 | Buffer<int, N> out; |
390 | std::ranges::uninitialized_copy(in.begin(), in.end(), out.begin(), out.end()); |
391 | } |
392 | |
393 | // (range) overload. |
394 | { |
395 | Buffer<int, N> out; |
396 | std::ranges::uninitialized_copy(in, out); |
397 | } |
398 | } |
399 | |
400 | // Test with an iterator that overloads operator== and operator!= as the input and output iterators |
401 | { |
402 | using T = int; |
403 | using Iterator = overload_compare_iterator<T*>; |
404 | const int N = 5; |
405 | |
406 | // input |
407 | { |
408 | char pool[sizeof(T) * N] = {0}; |
409 | T* p = reinterpret_cast<T*>(pool); |
410 | T* p_end = reinterpret_cast<T*>(pool) + N; |
411 | T array[N] = {1, 2, 3, 4, 5}; |
412 | std::ranges::uninitialized_copy(Iterator(array), Iterator(array + N), p, p_end); |
413 | for (int i = 0; i != N; ++i) { |
414 | assert(array[i] == p[i]); |
415 | } |
416 | } |
417 | |
418 | // output |
419 | { |
420 | char pool[sizeof(T) * N] = {0}; |
421 | T* p = reinterpret_cast<T*>(pool); |
422 | T* p_end = reinterpret_cast<T*>(pool) + N; |
423 | T array[N] = {1, 2, 3, 4, 5}; |
424 | std::ranges::uninitialized_copy(array, array + N, Iterator(p), Iterator(p_end)); |
425 | for (int i = 0; i != N; ++i) { |
426 | assert(array[i] == p[i]); |
427 | } |
428 | } |
429 | } |
430 | |
431 | return 0; |
432 | } |
433 | |