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_swap; |
13 | |
14 | #include <iterator> |
15 | |
16 | #include <array> |
17 | #include <cassert> |
18 | |
19 | #include "../unqualified_lookup_wrapper.h" |
20 | #include "test_iterators.h" |
21 | |
22 | using IterSwapT = decltype(std::ranges::iter_swap); |
23 | |
24 | struct HasIterSwap { |
25 | int &value_; |
26 | constexpr explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); } |
27 | |
28 | friend constexpr void iter_swap(HasIterSwap& a, HasIterSwap& b) { |
29 | a.value_ = 1; |
30 | b.value_ = 1; |
31 | } |
32 | friend constexpr void iter_swap(HasIterSwap& a, int& b) { |
33 | a.value_ = 2; |
34 | b = 2; |
35 | } |
36 | }; |
37 | |
38 | static_assert( std::is_invocable_v<IterSwapT, HasIterSwap&, HasIterSwap&>); |
39 | static_assert( std::is_invocable_v<IterSwapT, HasIterSwap&, int&>); |
40 | static_assert(!std::is_invocable_v<IterSwapT, int&, HasIterSwap&>); |
41 | |
42 | static_assert( std::is_invocable_v<IterSwapT&, HasIterSwap&, HasIterSwap&>); |
43 | static_assert( std::is_invocable_v<IterSwapT&, HasIterSwap&, int&>); |
44 | static_assert(!std::is_invocable_v<IterSwapT&, int&, HasIterSwap&>); |
45 | |
46 | static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, HasIterSwap&>); |
47 | static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, int&>); |
48 | static_assert(!std::is_invocable_v<IterSwapT&&, int&, HasIterSwap&>); |
49 | |
50 | struct StructWithNotMoreSpecializedIterSwap { |
51 | friend void iter_swap(auto&, auto&); |
52 | }; |
53 | |
54 | static_assert( |
55 | !std::is_invocable_v<IterSwapT, StructWithNotMoreSpecializedIterSwap&, StructWithNotMoreSpecializedIterSwap&>); |
56 | |
57 | struct NodiscardIterSwap { |
58 | [[nodiscard]] friend int iter_swap(NodiscardIterSwap&, NodiscardIterSwap&) { return 0; } |
59 | }; |
60 | |
61 | void ensureVoidCast(NodiscardIterSwap& a, NodiscardIterSwap& b) { std::ranges::iter_swap(a, b); } |
62 | |
63 | struct HasRangesSwap { |
64 | int &value_; |
65 | constexpr explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); } |
66 | |
67 | friend constexpr void swap(HasRangesSwap& a, HasRangesSwap& b) { |
68 | a.value_ = 1; |
69 | b.value_ = 1; |
70 | } |
71 | friend constexpr void swap(HasRangesSwap& a, int& b) { |
72 | a.value_ = 2; |
73 | b = 2; |
74 | } |
75 | }; |
76 | |
77 | struct HasRangesSwapWrapper { |
78 | using value_type = HasRangesSwap; |
79 | |
80 | HasRangesSwap &value_; |
81 | constexpr explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {} |
82 | |
83 | constexpr HasRangesSwap& operator*() const { return value_; } |
84 | }; |
85 | |
86 | static_assert( std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, HasRangesSwapWrapper&>); |
87 | // Does not satisfy swappable_with, even though swap(X, Y) is valid. |
88 | static_assert(!std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, int&>); |
89 | static_assert(!std::is_invocable_v<IterSwapT, int&, HasRangesSwapWrapper&>); |
90 | |
91 | struct B; |
92 | |
93 | struct A { |
94 | bool value = false; |
95 | constexpr A& operator=(const B&) { |
96 | value = true; |
97 | return *this; |
98 | }; |
99 | }; |
100 | |
101 | struct B { |
102 | bool value = false; |
103 | constexpr B& operator=(const A&) { |
104 | value = true; |
105 | return *this; |
106 | }; |
107 | }; |
108 | |
109 | struct MoveOnly2; |
110 | |
111 | struct MoveOnly1 { |
112 | bool value = false; |
113 | |
114 | MoveOnly1() = default; |
115 | MoveOnly1(MoveOnly1&&) = default; |
116 | MoveOnly1& operator=(MoveOnly1&&) = default; |
117 | MoveOnly1(const MoveOnly1&) = delete; |
118 | MoveOnly1& operator=(const MoveOnly1&) = delete; |
119 | |
120 | constexpr MoveOnly1& operator=(MoveOnly2 &&) { |
121 | value = true; |
122 | return *this; |
123 | }; |
124 | }; |
125 | |
126 | struct MoveOnly2 { |
127 | bool value = false; |
128 | |
129 | MoveOnly2() = default; |
130 | MoveOnly2(MoveOnly2&&) = default; |
131 | MoveOnly2& operator=(MoveOnly2&&) = default; |
132 | MoveOnly2(const MoveOnly2&) = delete; |
133 | MoveOnly2& operator=(const MoveOnly2&) = delete; |
134 | |
135 | constexpr MoveOnly2& operator=(MoveOnly1 &&) { |
136 | value = true; |
137 | return *this; |
138 | }; |
139 | }; |
140 | |
141 | constexpr bool test() |
142 | { |
143 | { |
144 | int value1 = 0; |
145 | int value2 = 0; |
146 | HasIterSwap a(value1), b(value2); |
147 | std::ranges::iter_swap(a, b); |
148 | assert(value1 == 1 && value2 == 1); |
149 | } |
150 | { |
151 | int value1 = 0; |
152 | int value2 = 0; |
153 | HasRangesSwap c(value1), d(value2); |
154 | HasRangesSwapWrapper cWrapper(c), dWrapper(d); |
155 | std::ranges::iter_swap(cWrapper, dWrapper); |
156 | assert(value1 == 1 && value2 == 1); |
157 | } |
158 | { |
159 | int value1 = 0; |
160 | int value2 = 0; |
161 | HasRangesSwap c(value1), d(value2); |
162 | std::ranges::iter_swap(HasRangesSwapWrapper(c), HasRangesSwapWrapper(d)); |
163 | assert(value1 == 1 && value2 == 1); |
164 | } |
165 | { |
166 | A e; B f; |
167 | A *ePtr = &e; |
168 | B *fPtr = &f; |
169 | std::ranges::iter_swap(ePtr, fPtr); |
170 | assert(e.value && f.value); |
171 | } |
172 | { |
173 | MoveOnly1 g; MoveOnly2 h; |
174 | std::ranges::iter_swap(&g, &h); |
175 | assert(g.value && h.value); |
176 | } |
177 | { |
178 | auto arr = std::array<move_tracker, 2>(); |
179 | std::ranges::iter_swap(arr.begin(), arr.begin() + 1); |
180 | if (std::is_constant_evaluated()) { |
181 | assert(arr[0].moves() == 1 && arr[1].moves() == 3); |
182 | } else { |
183 | assert(arr[0].moves() == 1 && arr[1].moves() == 2); |
184 | } |
185 | } |
186 | { |
187 | int buff[2] = {1, 2}; |
188 | std::ranges::iter_swap(buff + 0, buff + 1); |
189 | assert(buff[0] == 2 && buff[1] == 1); |
190 | |
191 | std::ranges::iter_swap(cpp20_input_iterator(buff), cpp20_input_iterator(buff + 1)); |
192 | assert(buff[0] == 1 && buff[1] == 2); |
193 | |
194 | std::ranges::iter_swap(cpp17_input_iterator(buff), cpp17_input_iterator(buff + 1)); |
195 | assert(buff[0] == 2 && buff[1] == 1); |
196 | |
197 | std::ranges::iter_swap(forward_iterator(buff), forward_iterator(buff + 1)); |
198 | assert(buff[0] == 1 && buff[1] == 2); |
199 | |
200 | std::ranges::iter_swap(bidirectional_iterator(buff), bidirectional_iterator(buff + 1)); |
201 | assert(buff[0] == 2 && buff[1] == 1); |
202 | |
203 | std::ranges::iter_swap(random_access_iterator(buff), random_access_iterator(buff + 1)); |
204 | assert(buff[0] == 1 && buff[1] == 2); |
205 | |
206 | std::ranges::iter_swap(contiguous_iterator(buff), contiguous_iterator(buff + 1)); |
207 | assert(buff[0] == 2 && buff[1] == 1); |
208 | } |
209 | return true; |
210 | } |
211 | |
212 | static_assert(!std::is_invocable_v<IterSwapT, int*>); // too few arguments |
213 | static_assert(!std::is_invocable_v<IterSwapT, int*, int*, int*>); // too many arguments |
214 | static_assert(!std::is_invocable_v<IterSwapT, int, int*>); |
215 | static_assert(!std::is_invocable_v<IterSwapT, int*, int>); |
216 | static_assert(!std::is_invocable_v<IterSwapT, void*, void*>); |
217 | |
218 | // Test ADL-proofing. |
219 | struct Incomplete; |
220 | template<class T> struct Holder { T t; }; |
221 | static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**, Holder<Incomplete>**>); |
222 | static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**, Holder<Incomplete>**&>); |
223 | static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**&, Holder<Incomplete>**>); |
224 | static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**&, Holder<Incomplete>**&>); |
225 | |
226 | int main(int, char**) |
227 | { |
228 | test(); |
229 | static_assert(test()); |
230 | |
231 | return 0; |
232 | } |
233 | |