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// REQUIRES: std-at-least-c++23
10
11// <ranges>
12
13// friend constexpr decltype(auto) iter_move(const iterator& x);
14
15#include <ranges>
16
17#include <algorithm>
18#include <array>
19#include <cassert>
20#include <cstddef>
21#include <utility>
22#include <vector>
23
24#include "../types.h"
25
26class MoveOnlyInt {
27public:
28 enum Status { constructed, move_constructed, moved_from_this };
29
30 MoveOnlyInt() = default;
31 constexpr MoveOnlyInt(int val) : val_(val) {}
32
33 constexpr MoveOnlyInt(MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) {
34 other.val_ = -1;
35 other.status_ = moved_from_this;
36 }
37
38 constexpr MoveOnlyInt(const MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) {
39 other.val_ = -1;
40 other.status_ = moved_from_this;
41 }
42
43 MoveOnlyInt(const MoveOnlyInt&) { assert(false); } // Should never be called in this test.
44
45 MoveOnlyInt& operator=(MoveOnlyInt&&) { // Should never be called in this test.
46 assert(false);
47 return *this;
48 }
49
50 constexpr bool was_normally_constructed() const { return status_ == constructed; }
51 constexpr bool was_move_constructed() const { return status_ == move_constructed; }
52 constexpr bool was_moved_from() const { return status_ == moved_from_this; }
53
54 friend constexpr bool operator==(const MoveOnlyInt& left, int right) { return left.val_ == right; }
55 friend constexpr bool operator==(const MoveOnlyInt& left, const MoveOnlyInt& right) {
56 return left.val_ == right.val_;
57 }
58
59private:
60 mutable int val_ = -1;
61 mutable Status status_ = constructed;
62};
63
64static_assert(std::movable<MoveOnlyInt>);
65
66struct ProxyRvalueRef {
67 MoveOnlyInt&& val;
68};
69
70class CommonProxyRvalueRef {
71public:
72 constexpr CommonProxyRvalueRef(ProxyRvalueRef i) : val_(std::move(i.val)) {}
73 constexpr CommonProxyRvalueRef(MoveOnlyInt i) : val_(std::move(i)) {}
74
75 constexpr MoveOnlyInt&& get() { return std::move(val_); }
76
77private:
78 MoveOnlyInt val_;
79};
80
81template <template <class> class TQual, template <class> class UQual>
82struct std::basic_common_reference<ProxyRvalueRef, MoveOnlyInt, TQual, UQual> {
83 using type = CommonProxyRvalueRef;
84};
85
86template <template <class> class TQual, template <class> class UQual>
87struct std::basic_common_reference<MoveOnlyInt, ProxyRvalueRef, TQual, UQual> {
88 using type = CommonProxyRvalueRef;
89};
90
91static_assert(std::common_reference_with<MoveOnlyInt&&, ProxyRvalueRef>);
92static_assert(std::common_reference_with<MoveOnlyInt&&, CommonProxyRvalueRef>);
93
94class ProxyIter {
95public:
96 using value_type = MoveOnlyInt;
97 using difference_type = std::ptrdiff_t;
98
99 constexpr ProxyIter() : ptr_(nullptr) {}
100 constexpr explicit ProxyIter(MoveOnlyInt* it) : ptr_(std::move(it)) {}
101
102 constexpr decltype(auto) operator*() const { return *ptr_; }
103
104 constexpr ProxyIter& operator++() {
105 ++ptr_;
106 return *this;
107 }
108
109 constexpr ProxyIter operator++(int) {
110 ProxyIter copy = *this;
111 ++ptr_;
112 return copy;
113 }
114
115 constexpr ProxyIter& operator--() {
116 --ptr_;
117 return *this;
118 }
119
120 constexpr ProxyIter operator--(int) {
121 ProxyIter copy = *this;
122 --ptr_;
123 return copy;
124 }
125
126 friend bool operator==(const ProxyIter&, const ProxyIter&) = default;
127
128 friend constexpr ProxyRvalueRef iter_move(const ProxyIter iter) {
129 return ProxyRvalueRef{std::ranges::iter_move(iter.ptr_)};
130 }
131
132private:
133 MoveOnlyInt* ptr_;
134};
135
136static_assert(std::forward_iterator<ProxyIter>);
137
138template <std::forward_iterator Iter>
139class IterMoveTrackingIterator {
140public:
141 using value_type = std::iter_value_t<Iter>;
142 using difference_type = std::iter_difference_t<Iter>;
143
144 IterMoveTrackingIterator() = default;
145 constexpr explicit IterMoveTrackingIterator(Iter iter, bool* flag = nullptr) : iter_(std::move(iter)), flag_(flag) {}
146
147 constexpr IterMoveTrackingIterator& operator++() {
148 ++iter_;
149 return *this;
150 }
151
152 constexpr IterMoveTrackingIterator operator++(int) {
153 auto tmp = *this;
154 ++*this;
155 return tmp;
156 }
157
158 constexpr decltype(auto) operator*() const { return *iter_; }
159
160 constexpr bool operator==(const IterMoveTrackingIterator& other) const { return iter_ == other.iter_; }
161
162 friend constexpr decltype(auto) iter_move(const IterMoveTrackingIterator& iter) {
163 assert(iter.flag_ != nullptr);
164 *iter.flag_ = true;
165 return std::ranges::iter_move(iter.iter_);
166 }
167
168private:
169 Iter iter_ = Iter();
170 bool* flag_ = nullptr;
171};
172
173static_assert(std::forward_iterator<IterMoveTrackingIterator<int*>> &&
174 !std::bidirectional_iterator<IterMoveTrackingIterator<int*>>);
175
176constexpr bool test() {
177 { // Test `iter_move` when result is true rvalue reference. Test return types.
178 using V = std::array<std::array<char, 1>, 2>;
179 using Pattern = std::array<char, 1>;
180 using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
181
182 JWV jwv(V{._M_elems: {{'0'}, {'1'}}}, Pattern{','});
183
184 {
185 auto it = jwv.begin();
186 std::same_as<char&&> decltype(auto) v_rref1 = iter_move(it);
187 std::same_as<char&&> decltype(auto) v_rref2 = iter_move(std::as_const(it));
188 std::same_as<char&&> decltype(auto) v_rref3 = std::ranges::iter_move(it);
189 std::same_as<char&&> decltype(auto) v_rref4 = std::ranges::iter_move(std::as_const(it));
190 assert(std::ranges::equal(std::array{v_rref1, v_rref2, v_rref3, v_rref4}, std::views::repeat('0', 4)));
191
192 ++it; // `it` points to element of `Pattern` from here
193 std::same_as<char&&> decltype(auto) pattern_rref1 = iter_move(it);
194 std::same_as<char&&> decltype(auto) pattern_rref2 = iter_move(std::as_const(it));
195 std::same_as<char&&> decltype(auto) pattern_rref3 = std::ranges::iter_move(it);
196 std::same_as<char&&> decltype(auto) pattern_rref4 = std::ranges::iter_move(std::as_const(it));
197 assert(std::ranges::equal(
198 std::array{pattern_rref1, pattern_rref2, pattern_rref3, pattern_rref4}, std::views::repeat(',', 4)));
199 }
200
201 {
202 auto cit = std::prev(std::as_const(jwv).end());
203 std::same_as<const char&&> decltype(auto) cv_rref1 = iter_move(cit);
204 std::same_as<const char&&> decltype(auto) cv_rref2 = iter_move(std::as_const(cit));
205 std::same_as<const char&&> decltype(auto) cv_rref3 = std::ranges::iter_move(cit);
206 std::same_as<const char&&> decltype(auto) cv_rref4 = std::ranges::iter_move(std::as_const(cit));
207 assert(std::ranges::equal(std::array{cv_rref1, cv_rref2, cv_rref3, cv_rref4}, std::views::repeat('1', 4)));
208
209 cit--; // `cit` points to element of `Pattern` from here
210 std::same_as<const char&&> decltype(auto) cpattern_rref1 = iter_move(cit);
211 std::same_as<const char&&> decltype(auto) cpattern_rref2 = iter_move(std::as_const(cit));
212 std::same_as<const char&&> decltype(auto) cpattern_rref3 = std::ranges::iter_move(cit);
213 std::same_as<const char&&> decltype(auto) cpattern_rref4 = std::ranges::iter_move(std::as_const(cit));
214 assert(std::ranges::equal(
215 std::array{cpattern_rref1, cpattern_rref2, cpattern_rref3, cpattern_rref4}, std::views::repeat(',', 4)));
216 }
217 }
218
219 { // Test `iter_move` when result is true rvalue reference. Test moving.
220 using Inner = std::vector<MoveOnlyInt>;
221 using V = std::vector<Inner>;
222 using Pattern = std::vector<MoveOnlyInt>;
223 using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
224
225 V v;
226 v.reserve(n: 2);
227 v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
228 v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
229 JWV jwv(std::move(v), std::ranges::to<Pattern>(std::views::iota(4, 12)));
230 assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_normally_constructed));
231
232 {
233 std::vector<MoveOnlyInt> values;
234 values.reserve(n: 8);
235
236 auto it = jwv.begin();
237 values.emplace_back(iter_move(it));
238 ++it;
239 values.emplace_back(iter_move(std::as_const(it)));
240 it++;
241 values.emplace_back(std::ranges::iter_move(it));
242 ++it;
243 values.emplace_back(std::ranges::iter_move(std::as_const(it)));
244 it++; // `it` points to element of `Pattern` from here
245 values.emplace_back(iter_move(it));
246 ++it;
247 values.emplace_back(iter_move(std::as_const(it)));
248 it++;
249 values.emplace_back(std::ranges::iter_move(it));
250 ++it;
251 values.emplace_back(std::ranges::iter_move(std::as_const(it)));
252
253 assert(std::ranges::equal(values, std::views::iota(0, 8)));
254 assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
255 }
256
257 {
258 std::vector<MoveOnlyInt> values;
259 values.reserve(n: 8);
260
261 auto cit = std::prev(std::as_const(jwv).end());
262 values.emplace_back(iter_move(cit));
263 cit--;
264 values.emplace_back(iter_move(std::as_const(cit)));
265 --cit;
266 values.emplace_back(std::ranges::iter_move(cit));
267 cit--;
268 values.emplace_back(std::ranges::iter_move(std::as_const(cit)));
269 --cit; // `it` points to element of `Pattern` from here
270 values.emplace_back(iter_move(cit));
271 cit--;
272 values.emplace_back(iter_move(std::as_const(cit)));
273 --cit;
274 values.emplace_back(std::ranges::iter_move(cit));
275 cit--;
276 values.emplace_back(std::ranges::iter_move(std::as_const(cit)));
277
278 assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
279 assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
280 }
281
282 assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_moved_from));
283 }
284
285 { // Test `iter_move` when result is proxy rvalue reference type, which is different from
286 // range_rvalue_reference_t<InnerRng> and range_rvalue_reference_t<Pattern>.
287 using Inner = std::vector<MoveOnlyInt>;
288 using V = std::vector<Inner>;
289 using Pattern = std::ranges::subrange<ProxyIter, ProxyIter>;
290 using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
291
292 static_assert(!std::same_as<std::ranges::range_rvalue_reference_t<V>, std::ranges::range_rvalue_reference_t<JWV>>);
293 static_assert(
294 !std::same_as<std::ranges::range_rvalue_reference_t<Pattern>, std::ranges::range_rvalue_reference_t<JWV>>);
295 static_assert(std::same_as<CommonProxyRvalueRef, std::ranges::range_rvalue_reference_t<JWV>>);
296
297 V v;
298 v.reserve(n: 2);
299 v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
300 v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
301
302 auto pattern = std::ranges::to<std::vector<MoveOnlyInt>>(std::views::iota(4, 12));
303 Pattern pattern_as_subrange(ProxyIter{pattern.data()}, ProxyIter{pattern.data() + pattern.size()});
304
305 JWV jwv(std::move(v), pattern_as_subrange);
306 assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_normally_constructed));
307
308 {
309 std::vector<MoveOnlyInt> values;
310 values.reserve(n: 8);
311
312 auto it = jwv.begin();
313 std::same_as<CommonProxyRvalueRef> decltype(auto) rref1 = iter_move(it);
314 values.emplace_back(rref1.get());
315 ++it;
316 std::same_as<CommonProxyRvalueRef> decltype(auto) rref2 = iter_move(std::as_const(it));
317 values.emplace_back(rref2.get());
318 it++;
319 std::same_as<CommonProxyRvalueRef> decltype(auto) rref3 = std::ranges::iter_move(it);
320 values.emplace_back(rref3.get());
321 ++it;
322 std::same_as<CommonProxyRvalueRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(it));
323 values.emplace_back(rref4.get());
324 it++; // `it` points to element of `Pattern` from here
325 std::same_as<CommonProxyRvalueRef> decltype(auto) rref5 = iter_move(it);
326 values.emplace_back(rref5.get());
327 ++it;
328 std::same_as<CommonProxyRvalueRef> decltype(auto) rref6 = iter_move(std::as_const(it));
329 values.emplace_back(rref6.get());
330 it++;
331 std::same_as<CommonProxyRvalueRef> decltype(auto) rref7 = std::ranges::iter_move(it);
332 values.emplace_back(rref7.get());
333 ++it;
334 std::same_as<CommonProxyRvalueRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(it));
335 values.emplace_back(rref8.get());
336
337 assert(std::ranges::equal(values, std::views::iota(0, 8)));
338 assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
339 }
340
341 {
342 std::vector<MoveOnlyInt> values;
343 values.reserve(n: 8);
344
345 auto cit = std::prev(std::as_const(jwv).end());
346 std::same_as<CommonProxyRvalueRef> decltype(auto) rref1 = iter_move(cit);
347 values.emplace_back(rref1.get());
348 cit--;
349 std::same_as<CommonProxyRvalueRef> decltype(auto) rref2 = iter_move(std::as_const(cit));
350 values.emplace_back(rref2.get());
351 --cit;
352 std::same_as<CommonProxyRvalueRef> decltype(auto) rref3 = std::ranges::iter_move(cit);
353 values.emplace_back(rref3.get());
354 cit--;
355 std::same_as<CommonProxyRvalueRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(cit));
356 values.emplace_back(rref4.get());
357 --cit; // `it` points to element of `Pattern` from here
358 std::same_as<CommonProxyRvalueRef> decltype(auto) rref5 = iter_move(cit);
359 values.emplace_back(rref5.get());
360 cit--;
361 std::same_as<CommonProxyRvalueRef> decltype(auto) rref6 = iter_move(std::as_const(cit));
362 values.emplace_back(rref6.get());
363 --cit;
364 std::same_as<CommonProxyRvalueRef> decltype(auto) rref7 = std::ranges::iter_move(cit);
365 values.emplace_back(rref7.get());
366 cit--;
367 std::same_as<CommonProxyRvalueRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(cit));
368 values.emplace_back(rref8.get());
369
370 assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
371 assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
372 }
373
374 assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_moved_from));
375 }
376
377 { // Make sure `iter_move` calls underlying's iterator `iter_move` (not `std::move(*i)`).
378 using Inner = std::vector<int>;
379 using InnerTrackingIter = IterMoveTrackingIterator<Inner::iterator>;
380 using TrackingInner = std::ranges::subrange<InnerTrackingIter>;
381 using Pattern = std::array<int, 1>;
382 using PatternTrackingIter = IterMoveTrackingIterator<Pattern::iterator>;
383 using TrackingPattern = std::ranges::subrange<PatternTrackingIter>;
384 using JWV = std::ranges::join_with_view<std::span<TrackingInner>, TrackingPattern>;
385
386 std::array<Inner, 2> v{._M_elems: {{1}, {2}}};
387 Pattern pat{-1};
388
389 bool v_moved = false;
390 std::array<TrackingInner, 2> tracking_v{
391 TrackingInner(InnerTrackingIter(v[0].begin(), &v_moved), InnerTrackingIter(v[0].end())),
392 TrackingInner(InnerTrackingIter(v[1].begin()), InnerTrackingIter(v[1].end()))};
393
394 bool pat_moved = false;
395 TrackingPattern tracking_pat(PatternTrackingIter(pat.begin(), &pat_moved), PatternTrackingIter(pat.end()));
396
397 JWV jwv(tracking_v, tracking_pat);
398 auto it = jwv.begin();
399
400 // Test calling `iter_move` when `it` points to element of `v`
401 assert(!v_moved);
402 assert(iter_move(it) == 1);
403 assert(v_moved);
404
405 // Test calling `iter_move` when `it` points to element of `pat`
406 ++it;
407 assert(!pat_moved);
408 assert(iter_move(it) == -1);
409 assert(pat_moved);
410 }
411
412 return true;
413}
414
415int main(int, char**) {
416 test();
417 static_assert(test());
418
419 return 0;
420}
421

source code of libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp