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// template<class I>
12// unspecified iter_move;
13
14#include <algorithm>
15#include <array>
16#include <cassert>
17#include <iterator>
18#include <type_traits>
19#include <utility>
20
21#include "../unqualified_lookup_wrapper.h"
22
23using IterMoveT = decltype(std::ranges::iter_move);
24
25// Wrapper around an iterator for testing `iter_move` when an unqualified call to `iter_move` isn't
26// possible.
27template <typename I>
28class iterator_wrapper {
29public:
30 iterator_wrapper() = default;
31
32 constexpr explicit iterator_wrapper(I i) noexcept : base_(std::move(i)) {}
33
34 // `noexcept(false)` is used to check that this operator is called.
35 constexpr decltype(auto) operator*() const& noexcept(false) { return *base_; }
36
37 // `noexcept` is used to check that this operator is called.
38 constexpr auto&& operator*() && noexcept { return std::move(*base_); }
39
40 constexpr iterator_wrapper& operator++() noexcept {
41 ++base_;
42 return *this;
43 }
44
45 constexpr void operator++(int) noexcept { ++base_; }
46
47 constexpr bool operator==(iterator_wrapper const& other) const noexcept { return base_ == other.base_; }
48
49private:
50 I base_ = I{};
51};
52
53template <class I>
54iterator_wrapper(I) -> iterator_wrapper<I>;
55
56template <typename It, typename Out>
57constexpr void unqualified_lookup_move(It first_, It last_, Out result_first_, Out result_last_) {
58 auto first = ::check_unqualified_lookup::unqualified_lookup_wrapper{std::move(first_)};
59 auto last = ::check_unqualified_lookup::unqualified_lookup_wrapper{std::move(last_)};
60 auto result_first = ::check_unqualified_lookup::unqualified_lookup_wrapper{std::move(result_first_)};
61 auto result_last = ::check_unqualified_lookup::unqualified_lookup_wrapper{std::move(result_last_)};
62
63 static_assert(!noexcept(std::ranges::iter_move(first)), "unqualified-lookup case not being chosen");
64
65 for (; first != last && result_first != result_last; (void)++first, ++result_first) {
66 *result_first = std::ranges::iter_move(first);
67 }
68}
69
70template <typename It, typename Out>
71constexpr void lvalue_move(It first_, It last_, Out result_first_, Out result_last_) {
72 auto first = iterator_wrapper{std::move(first_)};
73 auto last = ::iterator_wrapper{std::move(last_)};
74 auto result_first = iterator_wrapper{std::move(result_first_)};
75 auto result_last = iterator_wrapper{std::move(result_last_)};
76
77 static_assert(!noexcept(std::ranges::iter_move(first)), "`operator*() const&` is not noexcept, and there's no hidden "
78 "friend iter_move.");
79
80 for (; first != last && result_first != result_last; (void)++first, ++result_first) {
81 *result_first = std::ranges::iter_move(first);
82 }
83}
84
85template <typename It, typename Out>
86constexpr void rvalue_move(It first_, It last_, Out result_first_, Out result_last_) {
87 auto first = iterator_wrapper{std::move(first_)};
88 auto last = iterator_wrapper{std::move(last_)};
89 auto result_first = iterator_wrapper{std::move(result_first_)};
90 auto result_last = iterator_wrapper{std::move(result_last_)};
91
92 static_assert(noexcept(std::ranges::iter_move(std::move(first))),
93 "`operator*() &&` is noexcept, and there's no hidden friend iter_move.");
94
95 for (; first != last && result_first != result_last; (void)++first, ++result_first) {
96 auto i = first;
97 *result_first = std::ranges::iter_move(std::move(i));
98 }
99}
100
101template <bool NoExcept>
102struct WithADL {
103 WithADL() = default;
104 constexpr int operator*() const { return 0; }
105 constexpr WithADL& operator++();
106 constexpr void operator++(int);
107 constexpr bool operator==(WithADL const&) const;
108 friend constexpr int iter_move(WithADL&&) noexcept(NoExcept) { return 0; }
109};
110
111template <bool NoExcept>
112struct WithoutADL {
113 WithoutADL() = default;
114 constexpr int operator*() const noexcept(NoExcept) { return 0; }
115 constexpr WithoutADL& operator++();
116 constexpr void operator++(int);
117 constexpr bool operator==(WithoutADL const&) const;
118};
119
120constexpr bool test() {
121 constexpr int full_size = 100;
122 constexpr int half_size = full_size / 2;
123 constexpr int reset = 0;
124 auto v1 = std::array<move_tracker, full_size>{};
125
126 auto move_counter_is = [](auto const n) { return [n](auto const& x) { return x.moves() == n; }; };
127
128 auto v2 = std::array<move_tracker, half_size>{};
129 unqualified_lookup_move(first_: v1.begin(), last_: v1.end(), result_first_: v2.begin(), result_last_: v2.end());
130 assert(std::all_of(v1.cbegin(), v1.cend(), move_counter_is(reset)));
131 assert(std::all_of(v2.cbegin(), v2.cend(), move_counter_is(1)));
132
133 auto v3 = std::array<move_tracker, half_size>{};
134 unqualified_lookup_move(v1.begin() + half_size, v1.end(), v3.begin(), v3.end());
135 assert(std::all_of(v1.cbegin(), v1.cend(), move_counter_is(reset)));
136 assert(std::all_of(v3.cbegin(), v3.cend(), move_counter_is(1)));
137
138 auto v4 = std::array<move_tracker, half_size>{};
139 unqualified_lookup_move(v3.begin(), v3.end(), v4.begin(), v4.end());
140 assert(std::all_of(v3.cbegin(), v3.cend(), move_counter_is(reset)));
141 assert(std::all_of(v4.cbegin(), v4.cend(), move_counter_is(2)));
142
143 lvalue_move(first_: v2.begin(), last_: v2.end(), result_first_: v1.begin() + half_size, result_last_: v1.end());
144 assert(std::all_of(v2.cbegin(), v2.cend(), move_counter_is(reset)));
145 assert(std::all_of(v1.cbegin() + half_size, v1.cend(), move_counter_is(2)));
146
147 lvalue_move(v4.begin(), v4.end(), v1.begin(), v1.end());
148 assert(std::all_of(v4.cbegin(), v4.cend(), move_counter_is(reset)));
149 assert(std::all_of(v1.cbegin(), v1.cbegin() + half_size, move_counter_is(3)));
150
151 rvalue_move(first_: v1.begin(), last_: v1.end(), result_first_: v2.begin(), result_last_: v2.end());
152 assert(std::all_of(v1.cbegin(), v1.cbegin() + half_size, move_counter_is(reset)));
153 assert(std::all_of(v2.cbegin(), v2.cend(), move_counter_is(4)));
154
155 rvalue_move(v1.begin() + half_size, v1.end(), v3.begin(), v3.end());
156 assert(std::all_of(v1.cbegin(), v1.cend(), move_counter_is(reset)));
157 assert(std::all_of(v3.cbegin(), v3.cend(), move_counter_is(3)));
158
159 auto unscoped = check_unqualified_lookup::unscoped_enum::a;
160 assert(std::ranges::iter_move(unscoped) == check_unqualified_lookup::unscoped_enum::a);
161 static_assert(!noexcept(std::ranges::iter_move(unscoped)));
162
163 auto scoped = check_unqualified_lookup::scoped_enum::a;
164 assert(std::ranges::iter_move(scoped) == nullptr);
165 static_assert(noexcept(std::ranges::iter_move(scoped)));
166
167 auto some_union = check_unqualified_lookup::some_union{.x: 0};
168 assert(std::ranges::iter_move(some_union) == 0);
169 static_assert(!noexcept(std::ranges::iter_move(some_union)));
170
171 // Check noexcept-correctness
172 static_assert(noexcept(std::ranges::iter_move(std::declval<WithADL<true>>())));
173 static_assert(!noexcept(std::ranges::iter_move(std::declval<WithADL<false>>())));
174 static_assert(noexcept(std::ranges::iter_move(std::declval<WithoutADL<true>>())));
175 static_assert(!noexcept(std::ranges::iter_move(std::declval<WithoutADL<false>>())));
176
177 return true;
178}
179
180static_assert(!std::is_invocable_v<IterMoveT, int*, int*>); // too many arguments
181static_assert(!std::is_invocable_v<IterMoveT, int>);
182
183// Test ADL-proofing.
184struct Incomplete;
185template<class T> struct Holder { T t; };
186static_assert(std::is_invocable_v<IterMoveT, Holder<Incomplete>**>);
187static_assert(std::is_invocable_v<IterMoveT, Holder<Incomplete>**&>);
188
189int main(int, char**)
190{
191 test();
192 static_assert(test());
193
194 return 0;
195}
196

source code of libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp