Horizon
Loading...
Searching...
No Matches
access.hpp
Go to the documentation of this file.
1
2// Range v3 library
3//
4// Copyright Eric Niebler 2013-present
5// Copyright Casey Carter 2016
6//
7// Use, modification and distribution is subject to the
8// Boost Software License, Version 1.0. (See accompanying
9// file LICENSE_1_0.txt or copy at
10// http://www.boost.org/LICENSE_1_0.txt)
11//
12// Project home: https://github.com/ericniebler/range-v3
13//
14
15#ifndef RANGES_V3_ITERATOR_ACCESS_HPP
16#define RANGES_V3_ITERATOR_ACCESS_HPP
17
18#include <iterator>
19#include <type_traits>
20#include <utility>
21
22#include <std/detail/associated_types.hpp>
23
24#include <meta/meta.hpp>
25
26#include <concepts/concepts.hpp>
27
29
31#include <range/v3/utility/static_const.hpp>
33
34#include <range/v3/detail/prologue.hpp>
35
36namespace ranges
37{
40
42 namespace detail
43 {
44 template<typename I,
45#ifdef RANGES_WORKAROUND_MSVC_683388
46 typename R = meta::conditional_t<
47 std::is_pointer<uncvref_t<I>>::value &&
48 std::is_array<std::remove_pointer_t<uncvref_t<I>>>::value,
49 std::add_lvalue_reference_t<std::remove_pointer_t<uncvref_t<I>>>,
50 decltype(*std::declval<I &>())>,
51#else
52 typename R = decltype(*std::declval<I &>()),
53#endif
54 typename = R &>
55 using iter_reference_t_ = R;
56
57#if defined(RANGES_DEEP_STL_INTEGRATION) && RANGES_DEEP_STL_INTEGRATION && \
58 !defined(RANGES_DOXYGEN_INVOKED)
59 template<typename T>
60 using iter_value_t_ =
61 typename meta::conditional_t<
62 is_std_iterator_traits_specialized_v<T>,
63 std::iterator_traits<T>,
64 indirectly_readable_traits<T>>::value_type;
65#else
66 template<typename T>
67 using iter_value_t_ = typename indirectly_readable_traits<T>::value_type;
68#endif
69 } // namespace detail
71
72 template<typename R>
73 using iter_reference_t = detail::iter_reference_t_<R>;
74
75 template<typename R>
76 using iter_value_t = detail::iter_value_t_<uncvref_t<R>>;
77
79 namespace _iter_move_
80 {
81#if RANGES_BROKEN_CPO_LOOKUP
82 void iter_move(); // unqualified name lookup block
83#endif
84
85 template<typename T>
86 decltype(iter_move(std::declval<T>())) try_adl_iter_move_(int);
87
88 template<typename T>
89 void try_adl_iter_move_(long);
90
91 template<typename T>
92 RANGES_INLINE_VAR constexpr bool is_adl_indirectly_movable_v =
93 !RANGES_IS_SAME(void, decltype(_iter_move_::try_adl_iter_move_<T>(42)));
94
95 struct fn
96 {
97 // clang-format off
98 template<typename I,
99 typename = detail::enable_if_t<is_adl_indirectly_movable_v<I &>>>
100#ifndef RANGES_WORKAROUND_CLANG_23135
101 constexpr
102#endif // RANGES_WORKAROUND_CLANG_23135
103 auto CPP_auto_fun(operator())(I &&i)(const)
104 (
105 return iter_move(i)
106 )
107
108 template<
109 typename I,
110 typename = detail::enable_if_t<!is_adl_indirectly_movable_v<I &>>,
111 typename R = iter_reference_t<I>>
112#ifndef RANGES_WORKAROUND_CLANG_23135
113 constexpr
114#endif // RANGES_WORKAROUND_CLANG_23135
115 auto CPP_auto_fun(operator())(I &&i)(const)
116 (
117 return static_cast<aux::move_t<R>>(aux::move(*i))
118 )
119 // clang-format on
120 };
121 } // namespace _iter_move_
123
124 RANGES_DEFINE_CPO(_iter_move_::fn, iter_move)
125
126
127 namespace detail
128 {
129 template<typename I, typename O>
130 auto is_indirectly_movable_(I & (*i)(), O & (*o)(), iter_value_t<I> * v = nullptr)
131 -> always_<std::true_type,
132 decltype(iter_value_t<I>(iter_move(i()))),
133 decltype(*v = iter_move(i())),
134 decltype(*o() = (iter_value_t<I> &&) * v),
135 decltype(*o() = iter_move(i()))>;
136 template<typename I, typename O>
137 auto is_indirectly_movable_(...) -> std::false_type;
138
139 template<typename I, typename O>
140 auto is_nothrow_indirectly_movable_(iter_value_t<I> * v) -> meta::bool_<
141 noexcept(iter_value_t<I>(iter_move(std::declval<I &>()))) &&
142 noexcept(*v = iter_move(std::declval<I &>())) &&
143 noexcept(*std::declval<O &>() = (iter_value_t<I> &&) * v) &&
144 noexcept(*std::declval<O &>() = iter_move(std::declval<I &>()))>;
145 template<typename I, typename O>
146 auto is_nothrow_indirectly_movable_(...) -> std::false_type;
147 } // namespace detail
149
150 template<typename I, typename O>
151 RANGES_INLINE_VAR constexpr bool is_indirectly_movable_v =
152 decltype(detail::is_indirectly_movable_<I, O>(nullptr, nullptr))::value;
153
154 template<typename I, typename O>
155 RANGES_INLINE_VAR constexpr bool is_nothrow_indirectly_movable_v =
156 decltype(detail::is_nothrow_indirectly_movable_<I, O>(nullptr))::value;
157
158 template<typename I, typename O>
159 struct is_indirectly_movable : meta::bool_<is_indirectly_movable_v<I, O>>
160 {};
161
162 template<typename I, typename O>
164 : meta::bool_<is_nothrow_indirectly_movable_v<I, O>>
165 {};
166
168 namespace _iter_swap_
169 {
170 struct nope
171 {};
172
173 // Q: Should std::reference_wrapper be considered a proxy wrt swapping rvalues?
174 // A: No. Its operator= is currently defined to reseat the references, so
175 // std::swap(ra, rb) already means something when ra and rb are (lvalue)
176 // reference_wrappers. That reseats the reference wrappers but leaves the
177 // referents unmodified. Treating rvalue reference_wrappers differently would
178 // be confusing.
179
180 // Q: Then why is it OK to "re"-define swap for pairs and tuples of references?
181 // A: Because as defined above, swapping an rvalue tuple of references has the
182 // same semantics as swapping an lvalue tuple of references. Rather than
183 // reseat the references, assignment happens *through* the references.
184
185 // Q: But I have an iterator whose operator* returns an rvalue
186 // std::reference_wrapper<T>. How do I make it model indirectly_swappable?
187 // A: With an overload of iter_swap.
188
189 // Intentionally create an ambiguity with std::iter_swap, which is
190 // unconstrained.
191 template<typename T, typename U>
192 nope iter_swap(T, U) = delete;
193
194#ifdef RANGES_WORKAROUND_MSVC_895622
195 nope iter_swap();
196#endif
197
198 template<typename T, typename U>
199 decltype(iter_swap(std::declval<T>(), std::declval<U>())) try_adl_iter_swap_(int);
200
201 template<typename T, typename U>
202 nope try_adl_iter_swap_(long);
203
204 // Test whether an overload of iter_swap for a T and a U can be found
205 // via ADL with the iter_swap overload above participating in the
206 // overload set. This depends on user-defined iter_swap overloads
207 // being a better match than the overload in namespace std.
208 template<typename T, typename U>
209 RANGES_INLINE_VAR constexpr bool is_adl_indirectly_swappable_v =
210 !RANGES_IS_SAME(nope, decltype(_iter_swap_::try_adl_iter_swap_<T, U>(42)));
211
212 struct fn
213 {
214 // *If* a user-defined iter_swap is found via ADL, call that:
215 template<typename T, typename U>
216 constexpr detail::enable_if_t<is_adl_indirectly_swappable_v<T, U>> operator()(
217 T && t, U && u) const noexcept(noexcept(iter_swap((T &&) t, (U &&) u)))
218 {
219 (void)iter_swap((T &&) t, (U &&) u);
220 }
221
222 // *Otherwise*, for readable types with swappable reference
223 // types, call ranges::swap(*a, *b)
224 template<typename I0, typename I1>
225 constexpr detail::enable_if_t<
226 !is_adl_indirectly_swappable_v<I0, I1> &&
227 is_swappable_with<iter_reference_t<I0>, iter_reference_t<I1>>::value>
228 operator()(I0 && a, I1 && b) const noexcept(noexcept(ranges::swap(*a, *b)))
229 {
230 ranges::swap(*a, *b);
231 }
232
233 // *Otherwise*, for readable types that are mutually
234 // indirectly_movable_storable, implement as:
235 // iter_value_t<T0> tmp = iter_move(a);
236 // *a = iter_move(b);
237 // *b = std::move(tmp);
238 template<typename I0, typename I1>
239 constexpr detail::enable_if_t<
240 !is_adl_indirectly_swappable_v<I0, I1> &&
241 !is_swappable_with<iter_reference_t<I0>, iter_reference_t<I1>>::value &&
242 is_indirectly_movable_v<I0, I1> && is_indirectly_movable_v<I1, I0>>
243 operator()(I0 && a, I1 && b) const
244 noexcept(is_nothrow_indirectly_movable_v<I0, I1> &&
245 is_nothrow_indirectly_movable_v<I1, I0>)
246 {
247 iter_value_t<I0> v0 = iter_move(a);
248 *a = iter_move(b);
249 *b = detail::move(v0);
250 }
251 };
252 } // namespace _iter_swap_
254
256 RANGES_DEFINE_CPO(_iter_swap_::fn, iter_swap)
257
258
259 namespace detail
260 {
261 template<typename T, typename U>
262 auto is_indirectly_swappable_(T & (*t)(), U & (*u)())
263 -> detail::always_<std::true_type, decltype(iter_swap(t(), u()))>;
264 template<typename T, typename U>
265 auto is_indirectly_swappable_(...) -> std::false_type;
266
267 template<typename T, typename U>
268 auto is_nothrow_indirectly_swappable_(int)
269 -> meta::bool_<noexcept(iter_swap(std::declval<T &>(), std::declval<U &>()))>;
270 template<typename T, typename U>
271 auto is_nothrow_indirectly_swappable_(long) -> std::false_type;
272 } // namespace detail
274
275 template<typename T, typename U>
276 RANGES_INLINE_VAR constexpr bool is_indirectly_swappable_v =
277 decltype(detail::is_indirectly_swappable_<T, U>(nullptr, nullptr))::value;
278
279 template<typename T, typename U>
280 RANGES_INLINE_VAR constexpr bool is_nothrow_indirectly_swappable_v =
281 decltype(detail::is_nothrow_indirectly_swappable_<T, U>(0))::value;
282
283 template<typename T, typename U>
284 struct is_indirectly_swappable : meta::bool_<is_indirectly_swappable_v<T, U>>
285 {};
286
287 template<typename T, typename U>
289 : meta::bool_<is_nothrow_indirectly_swappable_v<T, U>>
290 {};
291
292 namespace cpp20
293 {
294 using ranges::iter_move;
295 using ranges::iter_reference_t;
296 using ranges::iter_swap;
297 using ranges::iter_value_t;
298 } // namespace cpp20
300} // namespace ranges
301
302#include <range/v3/detail/epilogue.hpp>
303
304#endif // RANGES_V3_ITERATOR_ACCESS_HPP
std::integral_constant< bool, B > bool_
An integral constant wrapper for bool.
Definition meta.hpp:168
typename detail::_cond< If >::template invoke< Then, Else > conditional_t
Select one type or another depending on a compile-time Boolean.
Definition meta.hpp:1148
Tiny meta-programming library.
Definition access.hpp:160
Definition access.hpp:285
Definition access.hpp:165