xref: /llvm-project/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/arrow.pass.cpp (revision 6a664674990094c1b5d2e717256f08cb04485899)
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 
11 // constexpr InnerIter operator->() const
12 //   requires has-arrow<InnerIter> && copyable<InnerIter>;
13 
14 #include <cassert>
15 #include <ranges>
16 
17 #include "../types.h"
18 
19 template <class T>
20 concept HasArrow = std::input_iterator<T> && (std::is_pointer_v<T> || requires(T i) { i.operator->(); });
21 
22 template <class Base>
23 struct move_only_input_iter_with_arrow {
24   Base it_;
25 
26   using value_type = std::iter_value_t<Base>;
27   using difference_type = std::intptr_t;
28   using iterator_concept = std::input_iterator_tag;
29 
move_only_input_iter_with_arrowmove_only_input_iter_with_arrow30   constexpr move_only_input_iter_with_arrow(Base it) : it_(std::move(it)) {}
31   constexpr move_only_input_iter_with_arrow(move_only_input_iter_with_arrow&&) = default;
32   constexpr move_only_input_iter_with_arrow(const move_only_input_iter_with_arrow&) = delete;
33   constexpr move_only_input_iter_with_arrow& operator=(move_only_input_iter_with_arrow&&) = default;
34   constexpr move_only_input_iter_with_arrow& operator=(const move_only_input_iter_with_arrow&) = delete;
35 
operator ++move_only_input_iter_with_arrow36   constexpr move_only_input_iter_with_arrow& operator++() {
37     ++it_;
38     return *this;
39   }
operator ++move_only_input_iter_with_arrow40   constexpr void operator++(int) { ++it_; }
41 
operator *move_only_input_iter_with_arrow42   constexpr std::iter_reference_t<Base> operator*() const { return *it_; }
operator ->move_only_input_iter_with_arrow43   constexpr auto operator->() const
44     requires(HasArrow<Base> && std::copyable<Base>) {
45     return it_;
46   }
47 };
48 static_assert(!std::copyable<move_only_input_iter_with_arrow<int*>>);
49 static_assert(std::input_iterator<move_only_input_iter_with_arrow<int*>>);
50 
51 template <class Base>
52 struct move_iter_sentinel {
53   Base it_;
54   explicit move_iter_sentinel() = default;
move_iter_sentinelmove_iter_sentinel55   constexpr move_iter_sentinel(Base it) : it_(std::move(it)) {}
operator ==move_iter_sentinel56   constexpr bool operator==(const move_only_input_iter_with_arrow<Base>& other) const { return it_ == other.it_; }
57 };
58 static_assert(std::sentinel_for<move_iter_sentinel<int*>, move_only_input_iter_with_arrow<int*>>);
59 
60 struct MoveOnlyIterInner : BufferView<move_only_input_iter_with_arrow<Box*>, move_iter_sentinel<Box*>> {
61   using BufferView::BufferView;
62 
63   using iterator = move_only_input_iter_with_arrow<Box*>;
64   using sentinel = move_iter_sentinel<Box*>;
65 
beginMoveOnlyIterInner66   iterator begin() const { return data_; }
endMoveOnlyIterInner67   sentinel end() const { return sentinel{data_ + size_}; }
68 };
69 static_assert(std::ranges::input_range<MoveOnlyIterInner>);
70 
71 template <class Base>
72 struct arrow_input_iter {
73   Base it_;
74 
75   using value_type = std::iter_value_t<Base>;
76   using difference_type = std::intptr_t;
77   using iterator_concept = std::input_iterator_tag;
78 
79   arrow_input_iter() = default;
arrow_input_iterarrow_input_iter80   constexpr arrow_input_iter(Base it) : it_(std::move(it)) {}
81 
operator ++arrow_input_iter82   constexpr arrow_input_iter& operator++() {
83     ++it_;
84     return *this;
85   }
operator ++arrow_input_iter86   constexpr void operator++(int) { ++it_; }
87 
operator *arrow_input_iter88   constexpr std::iter_reference_t<Base> operator*() const { return *it_; }
operator ->arrow_input_iter89   constexpr auto operator->() const { return it_; }
90 
91   friend constexpr bool operator==(const arrow_input_iter& x, const arrow_input_iter& y) = default;
92 };
93 
94 using ArrowInner = BufferView<arrow_input_iter<Box*>>;
95 static_assert(std::ranges::input_range<ArrowInner>);
96 static_assert(HasArrow<std::ranges::iterator_t<ArrowInner>>);
97 
test()98 constexpr bool test() {
99   Box buffer[4][4] = {{{1111}, {2222}, {3333}, {4444}},
100                       {{555}, {666}, {777}, {888}},
101                       {{99}, {1010}, {1111}, {1212}},
102                       {{13}, {14}, {15}, {16}}};
103 
104   {
105     // Copyable input iterator with arrow.
106     using BoxView = ValueView<Box>;
107     ValueView<Box> children[4] = {BoxView(buffer[0]), BoxView(buffer[1]), BoxView(buffer[2]), BoxView(buffer[3])};
108     std::ranges::join_view jv(ValueView<ValueView<Box>>{children});
109     assert(jv.begin()->x == 1111);
110     static_assert(HasArrow<decltype(jv.begin())>);
111   }
112 
113   {
114     std::ranges::join_view jv(buffer);
115     assert(jv.begin()->x == 1111);
116     static_assert(HasArrow<decltype(jv.begin())>);
117   }
118 
119   {
120     const std::ranges::join_view jv(buffer);
121     assert(jv.begin()->x == 1111);
122     static_assert(HasArrow<decltype(jv.begin())>);
123   }
124 
125   {
126     // LWG3500 `join_view::iterator::operator->()` is bogus
127     // `operator->` should not be defined if inner iterator is not copyable
128     // has-arrow<InnerIter> && !copyable<InnerIter>
129     static_assert(HasArrow<move_only_input_iter_with_arrow<int*>>);
130     MoveOnlyIterInner inners[2] = {buffer[0], buffer[1]};
131     std::ranges::join_view jv{inners};
132     static_assert(HasArrow<decltype(std::ranges::begin(inners[0]))>);
133     static_assert(!HasArrow<decltype(jv.begin())>);
134   }
135 
136   {
137     // LWG3500 `join_view::iterator::operator->()` is bogus
138     // `operator->` should not be defined if inner iterator does not have `operator->`
139     // !has-arrow<InnerIter> && copyable<InnerIter>
140     using Inner = BufferView<forward_iterator<Box*>>;
141     Inner inners[2] = {buffer[0], buffer[1]};
142     std::ranges::join_view jv{inners};
143     static_assert(!HasArrow<decltype(std::ranges::begin(inners[0]))>);
144     static_assert(!HasArrow<decltype(jv.begin())>);
145   }
146 
147   {
148     // arrow returns inner iterator
149     ArrowInner inners[2] = {buffer[0], buffer[1]};
150     std::ranges::join_view jv{inners};
151     static_assert(HasArrow<decltype(std::ranges::begin(inners[0]))>);
152     static_assert(HasArrow<decltype(jv.begin())>);
153 
154     auto jv_it = jv.begin();
155     std::same_as<arrow_input_iter<Box*>> auto arrow_it = jv_it.operator->();
156     assert(arrow_it->x == 1111);
157   }
158   return true;
159 }
160 
main(int,char **)161 int main(int, char**) {
162   test();
163   static_assert(test());
164 
165   return 0;
166 }
167