//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y) // noexcept(noexcept(ranges::iter_swap(x.i_., y.i_.))) // requires indirectly_swappable>; #include #include #include #include #include "../types.h" namespace adl { template struct MaybeNoexceptIterator { using value_type = int; using difference_type = std::ptrdiff_t; value_type* ptr_ = nullptr; int* iter_swap_invocations_ = nullptr; constexpr MaybeNoexceptIterator() = default; constexpr explicit MaybeNoexceptIterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {} value_type& operator*() const { return *ptr_; } MaybeNoexceptIterator& operator++() { ++ptr_; return *this; } MaybeNoexceptIterator operator++(int) { MaybeNoexceptIterator prev = *this; ++ptr_; return prev; } MaybeNoexceptIterator& operator--() { --ptr_; return *this; } MaybeNoexceptIterator operator--(int) { MaybeNoexceptIterator prev = *this; --ptr_; return prev; } constexpr friend void iter_swap(MaybeNoexceptIterator a, MaybeNoexceptIterator) noexcept(IsNoexcept) { if (a.iter_swap_invocations_) { ++(*a.iter_swap_invocations_); } } friend bool operator==(const MaybeNoexceptIterator& lhs, const MaybeNoexceptIterator& rhs) { return lhs.ptr_ == rhs.ptr_; } }; template struct View : std::ranges::view_base { int* iter_swaps = nullptr; constexpr View() = default; constexpr View(int& iter_swap_invocations) : iter_swaps(&iter_swap_invocations) { } constexpr adl::MaybeNoexceptIterator begin() { return adl::MaybeNoexceptIterator(*iter_swaps); } constexpr adl::MaybeNoexceptIterator end() { return adl::MaybeNoexceptIterator(*iter_swaps); } }; } // namespace adl constexpr bool test() { // Can use `iter_swap` with `inner-iterator`; `View` is a forward range. { // Non-const iterator. { SplitViewDiff v("abc def", " "); auto segment = *v.begin(); auto i1 = segment.begin(); auto i2 = i1++; static_assert(std::is_void_v); assert(*i1 == 'b'); assert(*i2 == 'a'); iter_swap(i1, i2); assert(*i1 == 'a'); assert(*i2 == 'b'); // Note that `iter_swap` swaps characters in the actual underlying range. assert(*v.base().begin() == 'b'); } // Const iterator. { SplitViewDiff v("abc def", " "); auto segment = *v.begin(); auto i1 = segment.begin(); const auto i2 = i1++; static_assert(std::is_void_v); static_assert(std::is_void_v); assert(*i1 == 'b'); assert(*i2 == 'a'); iter_swap(i1, i2); assert(*i1 == 'a'); assert(*i2 == 'b'); assert(*v.base().begin() == 'b'); } } // Can use `iter_swap` with `inner-iterator`; `View` is an input range. { // Non-const iterator. { // Iterators belong to the same view. { SplitViewInput v("abc def", ' '); auto segment = *v.begin(); auto i1 = segment.begin(); auto i2 = i1; ++i1; static_assert(std::is_void_v); assert(*i1 == 'b'); // For an input view, all inner iterators are essentially thin proxies to the same underlying iterator. assert(*i2 == 'b'); iter_swap(i1, i2); assert(*i1 == 'b'); assert(*i2 == 'b'); } // Iterators belong to different views. { SplitViewInput v1("abc def", ' '); auto val1 = *v1.begin(); SplitViewInput v2 = v1; auto val2 = *v2.begin(); auto i1 = val1.begin(); auto i2 = val2.begin(); ++i1; assert(*i1 == 'b'); assert(*i2 == 'a'); iter_swap(i1, i2); assert(*i1 == 'a'); assert(*i2 == 'b'); } } // Const iterator. { SplitViewInput v("abc def", ' '); auto segment = *v.begin(); const auto i1 = segment.begin(); const auto i2 = i1; static_assert(std::is_void_v); assert(*i1 == 'a'); assert(*i2 == 'a'); iter_swap(i1, i2); assert(*i1 == 'a'); assert(*i2 == 'a'); } } // Ensure the `iter_swap` customization point is being used. { int iter_swap_invocations = 0; adl::View<> input(iter_swap_invocations); std::ranges::lazy_split_view, adl::View<>> v(input, adl::View<>()); auto segment = *v.begin(); auto i = segment.begin(); iter_swap(i, i); assert(iter_swap_invocations == 1); } // Check the `noexcept` specification. { { using ThrowingSplitView = std::ranges::lazy_split_view, adl::View>; using ThrowingValueType = std::ranges::iterator_t::value_type; using ThrowingIter = std::ranges::iterator_t; ASSERT_NOT_NOEXCEPT(std::ranges::iter_swap( std::declval>(), std::declval>())); ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); } { using NoexceptSplitView = std::ranges::lazy_split_view, adl::View>; using NoexceptValueType = std::ranges::iterator_t::value_type; using NoexceptIter = std::ranges::iterator_t; ASSERT_NOEXCEPT(std::ranges::iter_swap( std::declval>(), std::declval>())); ASSERT_NOEXCEPT(iter_swap(std::declval(), std::declval())); } } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }