|
- /// \file
- // Range v3 library
- //
- // Copyright Eric Niebler 2013-present
- // Copyright Casey Carter 2016
- //
- // Use, modification and distribution is subject to the
- // Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- //
- // Project home: https://github.com/ericniebler/range-v3
- //
-
- #ifndef RANGES_V3_ITERATOR_ACCESS_HPP
- #define RANGES_V3_ITERATOR_ACCESS_HPP
-
- #include <iterator>
- #include <type_traits>
- #include <utility>
-
- #include <std/detail/associated_types.hpp>
-
- #include <meta/meta.hpp>
-
- #include <concepts/concepts.hpp>
-
- #include <range/v3/range_fwd.hpp>
-
- #include <range/v3/utility/move.hpp>
- #include <range/v3/utility/static_const.hpp>
- #include <range/v3/utility/swap.hpp>
-
- namespace ranges
- {
- /// \addtogroup group-iterator
- /// @{
-
- /// \cond
- namespace detail
- {
- template<typename I,
- #ifdef RANGES_WORKAROUND_MSVC_683388
- typename R = detail::if_then_t<
- std::is_pointer<uncvref_t<I>>::value &&
- std::is_array<std::remove_pointer_t<uncvref_t<I>>>::value,
- std::add_lvalue_reference_t<std::remove_pointer_t<uncvref_t<I>>>,
- decltype(*std::declval<I &>())>,
- #else
- typename R = decltype(*std::declval<I &>()),
- #endif
- typename = R &>
- using iter_reference_t_ = R;
- } // namespace detail
- /// \endcond
-
- template<typename R>
- using iter_reference_t = detail::iter_reference_t_<R>;
-
- #if defined(RANGES_DEEP_STL_INTEGRATION) && RANGES_DEEP_STL_INTEGRATION && \
- !defined(RANGES_DOXYGEN_INVOKED)
- template<typename T>
- using iter_value_t =
- typename detail::if_then_t<detail::is_std_iterator_traits_specialized_v<T>,
- std::iterator_traits<T>,
- readable_traits<T>>::value_type;
- #else
- template<typename T>
- using iter_value_t = typename readable_traits<T>::value_type;
- #endif
-
- /// \cond
- namespace _iter_move_
- {
- #if RANGES_BROKEN_CPO_LOOKUP
- void iter_move(); // unqualified name lookup block
- #endif
-
- template<typename T>
- decltype(iter_move(std::declval<T>())) try_adl_iter_move_(int);
-
- template<typename T>
- void try_adl_iter_move_(long);
-
- template<typename T>
- RANGES_INLINE_VAR constexpr bool is_adl_indirectly_movable_v =
- !RANGES_IS_SAME(void, decltype(_iter_move_::try_adl_iter_move_<T>(42)));
-
- struct fn
- {
- // clang-format off
- template<typename I,
- typename = detail::enable_if_t<is_adl_indirectly_movable_v<I &>>>
- #ifndef RANGES_WORKAROUND_CLANG_23135
- constexpr
- #endif // RANGES_WORKAROUND_CLANG_23135
- auto CPP_auto_fun(operator())(I &&i)(const)
- (
- return iter_move(i)
- )
-
- template<
- typename I,
- typename = detail::enable_if_t<!is_adl_indirectly_movable_v<I &>>,
- typename R = iter_reference_t<I>>
- #ifndef RANGES_WORKAROUND_CLANG_23135
- constexpr
- #endif // RANGES_WORKAROUND_CLANG_23135
- auto CPP_auto_fun(operator())(I &&i)(const)
- (
- return static_cast<aux::move_t<R>>(aux::move(*i))
- )
- // clang-format on
- };
- } // namespace _iter_move_
- /// \endcond
-
- RANGES_DEFINE_CPO(_iter_move_::fn, iter_move)
-
- /// \cond
- namespace detail
- {
- template<typename I, typename O>
- auto is_indirectly_movable_(I & (*i)(), O & (*o)(), iter_value_t<I> * v = nullptr)
- -> always_<std::true_type, decltype(iter_value_t<I>(iter_move(i()))),
- decltype(*v = iter_move(i())),
- decltype(*o() = (iter_value_t<I> &&) * v),
- decltype(*o() = iter_move(i()))>;
- template<typename I, typename O>
- auto is_indirectly_movable_(...) -> std::false_type;
-
- template<typename I, typename O>
- auto is_nothrow_indirectly_movable_(iter_value_t<I> * v) -> meta::bool_<
- noexcept(iter_value_t<I>(iter_move(std::declval<I &>()))) &&
- noexcept(*v = iter_move(std::declval<I &>())) &&
- noexcept(*std::declval<O &>() = (iter_value_t<I> &&) * v) &&
- noexcept(*std::declval<O &>() = iter_move(std::declval<I &>()))>;
- template<typename I, typename O>
- auto is_nothrow_indirectly_movable_(...) -> std::false_type;
- } // namespace detail
- /// \endcond
-
- template<typename I, typename O>
- RANGES_INLINE_VAR constexpr bool is_indirectly_movable_v =
- decltype(detail::is_indirectly_movable_<I, O>(nullptr, nullptr))::value;
-
- template<typename I, typename O>
- RANGES_INLINE_VAR constexpr bool is_nothrow_indirectly_movable_v =
- decltype(detail::is_nothrow_indirectly_movable_<I, O>(nullptr))::value;
-
- template<typename I, typename O>
- struct is_indirectly_movable : meta::bool_<is_indirectly_movable_v<I, O>>
- {};
-
- template<typename I, typename O>
- struct is_nothrow_indirectly_movable
- : meta::bool_<is_nothrow_indirectly_movable_v<I, O>>
- {};
-
- /// \cond
- namespace _iter_swap_
- {
- struct nope
- {};
-
- // Q: Should std::reference_wrapper be considered a proxy wrt swapping rvalues?
- // A: No. Its operator= is currently defined to reseat the references, so
- // std::swap(ra, rb) already means something when ra and rb are (lvalue)
- // reference_wrappers. That reseats the reference wrappers but leaves the
- // referents unmodified. Treating rvalue reference_wrappers differently would
- // be confusing.
-
- // Q: Then why is it OK to "re"-define swap for pairs and tuples of references?
- // A: Because as defined above, swapping an rvalue tuple of references has the
- // same semantics as swapping an lvalue tuple of references. Rather than
- // reseat the references, assignment happens *through* the references.
-
- // Q: But I have an iterator whose operator* returns an rvalue
- // std::reference_wrapper<T>. How do I make it model indirectly_swappable?
- // A: With an overload of iter_swap.
-
- // Intentionally create an ambiguity with std::iter_swap, which is
- // unconstrained.
- template<typename T, typename U>
- nope iter_swap(T, U) = delete;
-
- #ifdef RANGES_WORKAROUND_MSVC_895622
- nope iter_swap();
- #endif
-
- template<typename T, typename U>
- decltype(iter_swap(std::declval<T>(), std::declval<U>())) try_adl_iter_swap_(int);
-
- template<typename T, typename U>
- nope try_adl_iter_swap_(long);
-
- // Test whether an overload of iter_swap for a T and a U can be found
- // via ADL with the iter_swap overload above participating in the
- // overload set. This depends on user-defined iter_swap overloads
- // being a better match than the overload in namespace std.
- template<typename T, typename U>
- RANGES_INLINE_VAR constexpr bool is_adl_indirectly_swappable_v =
- !RANGES_IS_SAME(nope, decltype(_iter_swap_::try_adl_iter_swap_<T, U>(42)));
-
- struct fn
- {
- // *If* a user-defined iter_swap is found via ADL, call that:
- template<typename T, typename U>
- constexpr detail::enable_if_t<is_adl_indirectly_swappable_v<T, U>> operator()(
- T && t, U && u) const noexcept(noexcept(iter_swap((T &&) t, (U &&) u)))
- {
- (void)iter_swap((T &&) t, (U &&) u);
- }
-
- // *Otherwise*, for readable types with swappable reference
- // types, call ranges::swap(*a, *b)
- template<typename I0, typename I1>
- constexpr detail::enable_if_t<
- !is_adl_indirectly_swappable_v<I0, I1> &&
- is_swappable_with<iter_reference_t<I0>, iter_reference_t<I1>>::value>
- operator()(I0 && a, I1 && b) const noexcept(noexcept(ranges::swap(*a, *b)))
- {
- ranges::swap(*a, *b);
- }
-
- // *Otherwise*, for readable types that are mutually
- // indirectly_movable_storable, implement as:
- // iter_value_t<T0> tmp = iter_move(a);
- // *a = iter_move(b);
- // *b = std::move(tmp);
- template<typename I0, typename I1>
- constexpr detail::enable_if_t<
- !is_adl_indirectly_swappable_v<I0, I1> &&
- !is_swappable_with<iter_reference_t<I0>, iter_reference_t<I1>>::value &&
- is_indirectly_movable_v<I0, I1> && is_indirectly_movable_v<I1, I0>>
- operator()(I0 && a, I1 && b) const
- noexcept(is_nothrow_indirectly_movable_v<I0, I1> &&
- is_nothrow_indirectly_movable_v<I1, I0>)
- {
- iter_value_t<I0> v0 = iter_move(a);
- *a = iter_move(b);
- *b = detail::move(v0);
- }
- };
- } // namespace _iter_swap_
- /// \endcond
-
- /// \relates _iter_swap_::fn
- RANGES_DEFINE_CPO(_iter_swap_::fn, iter_swap)
-
- /// \cond
- namespace detail
- {
- template<typename T, typename U>
- auto is_indirectly_swappable_(T & (*t)(), U & (*u)())
- -> detail::always_<std::true_type, decltype(iter_swap(t(), u()))>;
- template<typename T, typename U>
- auto is_indirectly_swappable_(...) -> std::false_type;
-
- template<typename T, typename U>
- auto is_nothrow_indirectly_swappable_(int)
- -> meta::bool_<noexcept(iter_swap(std::declval<T &>(), std::declval<U &>()))>;
- template<typename T, typename U>
- auto is_nothrow_indirectly_swappable_(long) -> std::false_type;
- } // namespace detail
- /// \endcond
-
- template<typename T, typename U>
- RANGES_INLINE_VAR constexpr bool is_indirectly_swappable_v =
- decltype(detail::is_indirectly_swappable_<T, U>(nullptr, nullptr))::value;
-
- template<typename T, typename U>
- RANGES_INLINE_VAR constexpr bool is_nothrow_indirectly_swappable_v =
- decltype(detail::is_nothrow_indirectly_swappable_<T, U>(0))::value;
-
- template<typename T, typename U>
- struct is_indirectly_swappable : meta::bool_<is_indirectly_swappable_v<T, U>>
- {};
-
- template<typename T, typename U>
- struct is_nothrow_indirectly_swappable
- : meta::bool_<is_nothrow_indirectly_swappable_v<T, U>>
- {};
-
- namespace cpp20
- {
- using ranges::iter_move;
- using ranges::iter_reference_t;
- using ranges::iter_swap;
- using ranges::iter_value_t;
- } // namespace cpp20
- /// @}
- } // namespace ranges
-
- #endif // RANGES_V3_ITERATOR_ACCESS_HPP
|