Horizon
access.hpp
Go to the documentation of this file.
1 // 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 
28 #include <range/v3/range_fwd.hpp>
29 
31 #include <range/v3/utility/static_const.hpp>
33 
34 #include <range/v3/detail/prologue.hpp>
35 
36 namespace 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
template(typename ActionFn, typename Rng)(concept(invocable_action_closure_)(ActionFn
\concept invocable_action_closure_
constexpr RANGES_INLINE_VAR bool is_indirectly_swappable_v
>iter_swap::fn
Definition: access.hpp:276
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
Definition: access.hpp:290