162 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// friend constexpr decltype(auto) iter_move(const inner-iterator& i)12// noexcept(noexcept(ranges::iter_move(i.i_.<current>)));13 14#include <iterator>15 16#include <cassert>17#include <type_traits>18#include <utility>19#include "../types.h"20 21namespace adl {22 23template <bool IsNoexcept = false>24struct MaybeNoexceptIterator {25 using value_type = int;26 using difference_type = std::ptrdiff_t;27 28 value_type* ptr_ = nullptr;29 int* iter_move_invocations_ = nullptr;30 31 constexpr MaybeNoexceptIterator() = default;32 constexpr explicit MaybeNoexceptIterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {}33 34 constexpr value_type& operator*() const { return *ptr_; }35 36 MaybeNoexceptIterator& operator++() { ++ptr_; return *this; }37 MaybeNoexceptIterator operator++(int) {38 MaybeNoexceptIterator prev = *this;39 ++ptr_;40 return prev;41 }42 43 constexpr MaybeNoexceptIterator& operator--() { --ptr_; return *this; }44 constexpr MaybeNoexceptIterator operator--(int) {45 MaybeNoexceptIterator prev = *this;46 --ptr_;47 return prev;48 }49 50 constexpr friend value_type&& iter_move(MaybeNoexceptIterator iter) noexcept(IsNoexcept) {51 if (iter.iter_move_invocations_) {52 ++(*iter.iter_move_invocations_);53 }54 return std::move(*iter);55 }56 57 friend bool operator==(const MaybeNoexceptIterator& lhs, const MaybeNoexceptIterator& rhs) { return lhs.ptr_ == rhs.ptr_; }58};59 60template <bool IsNoexcept = false>61struct View : std::ranges::view_base {62 static constexpr int N = 3;63 int a[N] = {0, 1, 2};64 int* iter_moves = nullptr;65 66 constexpr View() = default;67 constexpr View(int& iter_move_invocations) : iter_moves(&iter_move_invocations) {68 }69 70 constexpr adl::MaybeNoexceptIterator<IsNoexcept> begin() {71 return adl::MaybeNoexceptIterator<IsNoexcept>(a, *iter_moves);72 }73 constexpr adl::MaybeNoexceptIterator<IsNoexcept> end() {74 return adl::MaybeNoexceptIterator<IsNoexcept>(a + N, *iter_moves);75 }76};77 78} // namespace adl79 80constexpr bool test() {81 // Can use `iter_move` with `inner-iterator`; `View` is a forward range.82 {83 SplitViewForward v("abc def", " ");84 auto segment = *v.begin();85 86 // Non-const iterator.87 {88 auto i = segment.begin();89 static_assert(std::same_as<decltype(iter_move(i)), const char &&>);90 assert(iter_move(i) == 'a');91 }92 93 // Const iterator.94 {95 const auto i = segment.begin();96 static_assert(std::same_as<decltype(iter_move(i)), const char &&>);97 assert(iter_move(i) == 'a');98 }99 }100 101 // Can use `iter_move` with `inner-iterator`, `View` is an input range.102 {103 SplitViewInput v("abc def", ' ');104 auto segment = *v.begin();105 106 // Non-const iterator.107 {108 auto i = segment.begin();109 static_assert(std::same_as<decltype(iter_move(i)), char &&>);110 assert(iter_move(i) == 'a');111 }112 113 // Const iterator.114 {115 const auto i = segment.begin();116 static_assert(std::same_as<decltype(iter_move(i)), char &&>);117 assert(iter_move(i) == 'a');118 }119 }120 121 // Ensure the `iter_move` customization point is being used.122 {123 int iter_move_invocations = 0;124 adl::View<> input(iter_move_invocations);125 std::ranges::lazy_split_view<adl::View<>, adl::View<>> v(input, adl::View<>());126 127 auto segment = *v.begin();128 auto i = segment.begin();129 int x = iter_move(i);130 assert(x == 0);131 assert(iter_move_invocations == 1);132 }133 134 // Check the `noexcept` specification.135 {136 {137 using ThrowingSplitView = std::ranges::lazy_split_view<adl::View<false>, adl::View<false>>;138 using ThrowingValueType = std::ranges::iterator_t<ThrowingSplitView>::value_type;139 using ThrowingIter = std::ranges::iterator_t<ThrowingValueType>;140 ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval<adl::MaybeNoexceptIterator<false>>()));141 ASSERT_NOT_NOEXCEPT(iter_move(std::declval<ThrowingIter>()));142 }143 144 {145 using NoexceptSplitView = std::ranges::lazy_split_view<adl::View<true>, adl::View<true>>;146 using NoexceptValueType = std::ranges::iterator_t<NoexceptSplitView>::value_type;147 using NoexceptIter = std::ranges::iterator_t<NoexceptValueType>;148 ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval<adl::MaybeNoexceptIterator<true>>()));149 ASSERT_NOEXCEPT(iter_move(std::declval<NoexceptIter>()));150 }151 }152 153 return true;154}155 156int main(int, char**) {157 test();158 static_assert(test());159 160 return 0;161}162