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