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// namespace ranges {
14// template<nothrow-input-iterator InputIterator, nothrow-sentinel-for<InputIterator> Sentinel>
15// requires destructible<iter_value_t<InputIterator>>
16// constexpr InputIterator destroy(InputIterator first, Sentinel last) noexcept; // since C++20
17// template<nothrow-input-range InputRange>
18// requires destructible<range_value_t<InputRange>>
19// constexpr borrowed_iterator_t<InputRange> destroy(InputRange&& range) noexcept; // since C++20
20// }
21
22#include <cassert>
23#include <memory>
24#include <ranges>
25#include <type_traits>
26
27#include "test_iterators.h"
28#include "test_macros.h"
29
30// TODO(varconst): consolidate the ADL checks into a single file.
31// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
32// implementations are allowed to use a different mechanism to achieve this effect, so this check is
33// libc++-specific.
34LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::destroy)>);
35
36struct NotNothrowDtrable {
37 ~NotNothrowDtrable() noexcept(false) {}
38};
39static_assert(!std::is_invocable_v<decltype(std::ranges::destroy), NotNothrowDtrable*, NotNothrowDtrable*>);
40
41struct Counted {
42 int& count;
43
44 constexpr Counted(int& count_ref) : count(count_ref) { ++count; }
45 constexpr Counted(const Counted& rhs) : count(rhs.count) { ++count; }
46 constexpr ~Counted() { --count; }
47
48 friend void operator&(Counted) = delete;
49};
50
51template <class Iterator>
52constexpr void test() {
53 // (iterator + sentinel) overload.
54 {
55 constexpr int N = 5;
56 std::allocator<Counted> alloc;
57 using Traits = std::allocator_traits<decltype(alloc)>;
58 int counter = 0;
59
60 Counted* out = Traits::allocate(a&: alloc, n: N);
61 for (int i = 0; i != N; ++i) {
62 Traits::construct(a&: alloc, p: out + i, args&: counter);
63 }
64 assert(counter == N);
65
66 std::ranges::destroy(Iterator(out), Iterator(out + N));
67 assert(counter == 0);
68
69 Traits::deallocate(a&: alloc, p: out, n: N);
70 }
71
72 // (range) overload.
73 {
74 constexpr int N = 5;
75 std::allocator<Counted> alloc;
76 using Traits = std::allocator_traits<decltype(alloc)>;
77 int counter = 0;
78
79 Counted* out = Traits::allocate(a&: alloc, n: N);
80 for (int i = 0; i != N; ++i) {
81 Traits::construct(a&: alloc, p: out + i, args&: counter);
82 }
83 assert(counter == N);
84
85 auto range = std::ranges::subrange(Iterator(out), Iterator(out + N));
86 std::ranges::destroy(range);
87 assert(counter == 0);
88
89 Traits::deallocate(a&: alloc, p: out, n: N);
90 }
91}
92
93constexpr bool tests() {
94 test<Counted*>();
95 test<forward_iterator<Counted*>>();
96
97 return true;
98}
99
100constexpr bool test_arrays() {
101 // One-dimensional array, (iterator + sentinel) overload.
102 {
103 constexpr int N = 5;
104 constexpr int M = 3;
105
106 using Array = Counted[M];
107 std::allocator<Array> alloc;
108 using Traits = std::allocator_traits<decltype(alloc)>;
109 int counter = 0;
110
111 Array* buffer = Traits::allocate(a&: alloc, n: N);
112 for (int i = 0; i != N; ++i) {
113 Array& array_ref = *(buffer + i);
114 for (int j = 0; j != M; ++j) {
115 Traits::construct(a&: alloc, p: std::addressof(r&: array_ref[j]), args&: counter);
116 }
117 }
118 assert(counter == N * M);
119
120 std::ranges::destroy(buffer, buffer + N);
121 assert(counter == 0);
122
123 Traits::deallocate(a&: alloc, p: buffer, n: N);
124 }
125
126 // One-dimensional array, (range) overload.
127 {
128 constexpr int N = 5;
129 constexpr int A = 3;
130
131 using Array = Counted[A];
132 std::allocator<Array> alloc;
133 using Traits = std::allocator_traits<decltype(alloc)>;
134 int counter = 0;
135
136 Array* buffer = Traits::allocate(a&: alloc, n: N);
137 for (int i = 0; i != N; ++i) {
138 Array& array_ref = *(buffer + i);
139 for (int j = 0; j != A; ++j) {
140 Traits::construct(a&: alloc, p: std::addressof(r&: array_ref[j]), args&: counter);
141 }
142 }
143 assert(counter == N * A);
144
145 auto range = std::ranges::subrange(buffer, buffer + N);
146 std::ranges::destroy(range);
147 assert(counter == 0);
148
149 Traits::deallocate(a&: alloc, p: buffer, n: N);
150 }
151
152 // Multidimensional array, (iterator + sentinel ) overload.
153 {
154 constexpr int N = 5;
155 constexpr int A = 3;
156 constexpr int B = 3;
157
158 using Array = Counted[A][B];
159 std::allocator<Array> alloc;
160 using Traits = std::allocator_traits<decltype(alloc)>;
161 int counter = 0;
162
163 Array* buffer = Traits::allocate(a&: alloc, n: N);
164 for (int i = 0; i != N; ++i) {
165 Array& array_ref = *(buffer + i);
166 for (int j = 0; j != A; ++j) {
167 for (int k = 0; k != B; ++k) {
168 Traits::construct(a&: alloc, p: std::addressof(r&: array_ref[j][k]), args&: counter);
169 }
170 }
171 }
172 assert(counter == N * A * B);
173
174 std::ranges::destroy(buffer, buffer + N);
175 assert(counter == 0);
176
177 Traits::deallocate(a&: alloc, p: buffer, n: N);
178 }
179
180 // Multidimensional array, (range) overload.
181 {
182 constexpr int N = 5;
183 constexpr int A = 3;
184 constexpr int B = 3;
185
186 using Array = Counted[A][B];
187 std::allocator<Array> alloc;
188 using Traits = std::allocator_traits<decltype(alloc)>;
189 int counter = 0;
190
191 Array* buffer = Traits::allocate(a&: alloc, n: N);
192 for (int i = 0; i != N; ++i) {
193 Array& array_ref = *(buffer + i);
194 for (int j = 0; j != A; ++j) {
195 for (int k = 0; k != B; ++k) {
196 Traits::construct(a&: alloc, p: std::addressof(r&: array_ref[j][k]), args&: counter);
197 }
198 }
199 }
200 assert(counter == N * A * B);
201
202 std::ranges::destroy(buffer, buffer + N);
203 assert(counter == 0);
204
205 Traits::deallocate(a&: alloc, p: buffer, n: N);
206 }
207
208 return true;
209}
210
211int main(int, char**) {
212 tests();
213 test_arrays();
214
215 static_assert(tests());
216 // TODO: Until std::construct_at has support for arrays, it's impossible to test this
217 // in a constexpr context (see https://reviews.llvm.org/D114903).
218 // static_assert(test_arrays());
219
220 return 0;
221}
222

source code of libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy.pass.cpp