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, nothrow-forward-iterator O, nothrow-sentinel-for<O> S>
14// requires constructible_from<iter_value_t<O>, iter_reference_t<I>>
15// uninitialized_copy_n_result<I, O> uninitialized_copy_n(I ifirst, iter_difference_t<I> n, O ofirst, S olast); // since C++20
16
17#include <algorithm>
18#include <array>
19#include <cassert>
20#include <iterator>
21#include <memory>
22#include <ranges>
23#include <type_traits>
24
25#include "../buffer.h"
26#include "../counted.h"
27#include "../overload_compare_iterator.h"
28#include "MoveOnly.h"
29#include "test_iterators.h"
30#include "test_macros.h"
31
32// TODO(varconst): consolidate the ADL checks into a single file.
33// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
34// implementations are allowed to use a different mechanism to achieve this effect, so this check is
35// libc++-specific.
36LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_move_n)>);
37
38static_assert(std::is_invocable_v<decltype(std::ranges::uninitialized_move_n), int*, std::size_t, long*, long*>);
39struct NotConvertibleFromInt {};
40static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_move_n), int*, std::size_t, NotConvertibleFromInt*,
41 NotConvertibleFromInt*>);
42
43int main(int, char**) {
44 // An empty range -- no default constructors should be invoked.
45 {
46 Counted in[] = {Counted()};
47 Buffer<Counted, 1> out;
48 Counted::reset();
49
50 auto result = std::ranges::uninitialized_move_n(in, 0, out.begin(), out.end());
51 assert(Counted::current_objects == 0);
52 assert(Counted::total_objects == 0);
53 assert(Counted::total_copies == 0);
54 assert(result.in == in);
55 assert(result.out == out.begin());
56 }
57 Counted::reset();
58
59 // A range containing several objects.
60 {
61 constexpr int N = 5;
62 Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
63 Buffer<Counted, N> out;
64 Counted::reset();
65
66 auto result = std::ranges::uninitialized_move_n(in, N, out.begin(), out.end());
67 ASSERT_SAME_TYPE(decltype(result), std::ranges::uninitialized_move_n_result<Counted*, Counted*>);
68
69 assert(Counted::current_objects == N);
70 assert(Counted::total_objects == N);
71 assert(Counted::total_moves == N);
72 assert(Counted::total_copies == 0);
73
74 assert(std::equal(in, in + N, out.begin(), out.end()));
75 assert(result.in == in + N);
76 assert(result.out == out.end());
77
78 std::destroy(first: out.begin(), last: out.end());
79 }
80 Counted::reset();
81
82 // An exception is thrown while objects are being created -- the existing objects should stay
83 // valid. (iterator, sentinel) overload.
84#ifndef TEST_HAS_NO_EXCEPTIONS
85 {
86 constexpr int N = 3;
87 Counted in[] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
88 Buffer<Counted, 5> out;
89 Counted::reset();
90
91 Counted::throw_on = N; // When constructing out[3].
92 try {
93 std::ranges::uninitialized_move_n(in, 5, out.begin(), out.end());
94 assert(false);
95 } catch (...) {
96 }
97 assert(Counted::current_objects == 0);
98 assert(Counted::total_objects == N);
99 assert(Counted::total_moves == N);
100 assert(Counted::total_copies == 0);
101
102 std::destroy(first: out.begin(), last: out.begin() + N);
103 }
104 Counted::reset();
105
106#endif // TEST_HAS_NO_EXCEPTIONS
107
108 // Conversions.
109 {
110 constexpr int N = 3;
111 int in[N] = {1, 2, 3};
112 Buffer<double, N> out;
113
114 std::ranges::uninitialized_move_n(in, N, out.begin(), out.end());
115 assert(std::equal(in, in + N, out.begin(), out.end()));
116 }
117
118 // Destination range is shorter than the source range.
119 {
120 constexpr int M = 3;
121 constexpr int N = 5;
122 Counted in[N] = {Counted(1), Counted(2), Counted(3), Counted(4), Counted(5)};
123 Buffer<Counted, M> out;
124 Counted::reset();
125
126 auto result = std::ranges::uninitialized_move_n(in, N, out.begin(), out.end());
127 assert(Counted::current_objects == M);
128 assert(Counted::total_objects == M);
129 assert(Counted::total_moves == M);
130 assert(Counted::total_copies == 0);
131
132 assert(std::equal(in, in + M, out.begin(), out.end()));
133 assert(result.in == in + M);
134 assert(result.out == out.end());
135 }
136
137 // Ensure the `iter_move` customization point is being used.
138 {
139 constexpr int N = 3;
140 int in[N] = {1, 2, 3};
141 Buffer<int, N> out;
142 int iter_moves = 0;
143 adl::Iterator begin = adl::Iterator::TrackMoves(in, iter_moves);
144 adl::Iterator end = adl::Iterator::TrackMoves(in + N, iter_moves);
145
146 std::ranges::uninitialized_move(begin, end, out.begin(), out.end());
147 assert(iter_moves == 3);
148 iter_moves = 0;
149
150 std::ranges::subrange range(begin, end);
151 std::ranges::uninitialized_move(range, out);
152 assert(iter_moves == 3);
153 iter_moves = 0;
154 }
155
156 // Move-only iterators are supported.
157 {
158 using MoveOnlyIter = cpp20_input_iterator<const int*>;
159 static_assert(!std::is_copy_constructible_v<MoveOnlyIter>);
160
161 constexpr int N = 3;
162 int buffer[N] = {1, 2, 3};
163
164 MoveOnlyIter in(buffer);
165 Buffer<int, N> out;
166 std::ranges::uninitialized_move_n(std::move(in), N, out.begin(), out.end());
167 }
168
169 // MoveOnly types are supported
170 {
171 {
172 MoveOnly a[] = {1, 2, 3, 4};
173 Buffer<MoveOnly, 4> out;
174 std::ranges::uninitialized_move_n(std::begin(a), std::size(a), std::begin(out), std::end(out));
175 assert(std::ranges::equal(out, std::array<MoveOnly, 4>{1, 2, 3, 4}));
176 }
177 }
178
179 // Test with an iterator that overloads operator== and operator!= as the input and output iterators
180 {
181 using T = int;
182 using Iterator = overload_compare_iterator<T*>;
183 const int N = 5;
184
185 // input
186 {
187 char pool[sizeof(T) * N] = {0};
188 T* p = reinterpret_cast<T*>(pool);
189 T* p_end = reinterpret_cast<T*>(pool) + N;
190 T array[N] = {1, 2, 3, 4, 5};
191 std::ranges::uninitialized_move_n(Iterator(array), N, p, p_end);
192 for (int i = 0; i != N; ++i) {
193 assert(array[i] == p[i]);
194 }
195 }
196
197 // output
198 {
199 char pool[sizeof(T) * N] = {0};
200 T* p = reinterpret_cast<T*>(pool);
201 T* p_end = reinterpret_cast<T*>(pool) + N;
202 T array[N] = {1, 2, 3, 4, 5};
203 std::ranges::uninitialized_move_n(array, N, Iterator(p), Iterator(p_end));
204 for (int i = 0; i != N; ++i) {
205 assert(array[i] == p[i]);
206 }
207 }
208 }
209
210 return 0;
211}
212

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