@@ -39,50 +39,72 @@ template <std::ranges::forward_range R>
3939 return std::ranges::all_of (range, [&k](const auto & val) { return val == k; });
4040}
4141
42- // --- C++20 Concat View Utility ---
43-
44- namespace detail {
42+ // TODO: add tests
4543
44+ // / @brief A generic view that concatenates two ranges/views.
45+ // / @todo Replace with `std::views::concat` (Requires C++26)
4646template <std::ranges::view V1, std::ranges::view V2>
4747class concat_view : public std ::ranges::view_interface<concat_view<V1, V2>> {
4848public:
4949 concat_view () = default ;
5050
5151 constexpr concat_view (V1 v1, V2 v2) : _v1(std::move(v1)), _v2(std::move(v2)) {}
5252
53- class sentinel {
54- public:
55- sentinel () = default ;
53+ constexpr auto begin () {
54+ return iterator<false >(
55+ std::ranges::begin (_v1), std::ranges::end (_v1), std::ranges::begin (_v2)
56+ );
57+ }
5658
57- constexpr explicit sentinel (std::ranges::sentinel_t <V2> end2) : _end2(std::move(end2)) {}
59+ constexpr auto begin () const
60+ requires std::ranges::range<const V1> && std::ranges::range<const V2>
61+ {
62+ return iterator<true >(
63+ std::ranges::begin (_v1), std::ranges::end (_v1), std::ranges::begin (_v2)
64+ );
65+ }
5866
59- constexpr auto end2 () const {
60- return _end2 ;
61- }
67+ constexpr auto end () {
68+ return sentinel< false >( std::ranges::end (_v2)) ;
69+ }
6270
63- private:
64- std::ranges::sentinel_t <V2> _end2;
65- friend class concat_view ;
66- };
71+ constexpr auto end () const
72+ requires std::ranges::range<const V1> && std::ranges::range<const V2>
73+ {
74+ return sentinel<true >(std::ranges::end (_v2));
75+ }
6776
77+ private:
78+ V1 _v1;
79+ V2 _v2;
80+
81+ template <bool Const>
82+ class sentinel ; // forward declare
83+
84+ template <bool Const>
6885 class iterator {
86+ private:
87+ using BaseV1 = std::conditional_t <Const, const V1, V1>;
88+ using BaseV2 = std::conditional_t <Const, const V2, V2>;
89+
6990 public:
70- using difference_type = std::
71- common_type_t <std::ranges::range_difference_t <V1>, std::ranges::range_difference_t <V2>>;
72- using value_type =
73- std::common_type_t <std::ranges::range_value_t <V1>, std::ranges::range_value_t <V2>>;
91+ using difference_type = std::common_type_t <
92+ std::ranges::range_difference_t <BaseV1>,
93+ std::ranges::range_difference_t <BaseV2>>;
94+ using value_type = std::
95+ common_type_t <std::ranges::range_value_t <BaseV1>, std::ranges::range_value_t <BaseV2>>;
7496 using reference = std::common_reference_t <
75- std::ranges::range_reference_t <V1 >,
76- std::ranges::range_reference_t <V2 >>;
97+ std::ranges::range_reference_t <BaseV1 >,
98+ std::ranges::range_reference_t <BaseV2 >>;
7799 using iterator_category = std::forward_iterator_tag;
78100 using iterator_concept = std::forward_iterator_tag;
79101
80102 iterator () = default ;
81103
82104 constexpr iterator (
83- std::ranges::iterator_t <V1 > it1,
84- std::ranges::sentinel_t <V1 > end1,
85- std::ranges::iterator_t <V2 > it2
105+ std::ranges::iterator_t <BaseV1 > it1,
106+ std::ranges::sentinel_t <BaseV1 > end1,
107+ std::ranges::iterator_t <BaseV2 > it2
86108 )
87109 : _it1(std::move(it1)), _end1(std::move(end1)), _it2(std::move(it2)) {}
88110
@@ -112,41 +134,43 @@ class concat_view : public std::ranges::view_interface<concat_view<V1, V2>> {
112134 return _it1 == other._it1 && _it2 == other._it2 ;
113135 }
114136
115- constexpr bool operator ==(const sentinel& s) const {
137+ constexpr bool operator ==(const sentinel<Const> & s) const {
116138 return _it1 == _end1 && _it2 == s.end2 ();
117139 }
118140
119141 private:
120- std::ranges::iterator_t <V1> _it1;
121- std::ranges::sentinel_t <V1> _end1;
122- std::ranges::iterator_t <V2> _it2;
142+ std::ranges::iterator_t <BaseV1> _it1{};
143+ std::ranges::sentinel_t <BaseV1> _end1{};
144+ std::ranges::iterator_t <BaseV2> _it2{};
145+
146+ friend class concat_view ;
123147 };
124148
125- constexpr iterator begin () {
126- return iterator (std::ranges::begin (_v1), std::ranges::end (_v1), std::ranges::begin (_v2));
127- }
149+ template <bool Const>
150+ class sentinel {
151+ private:
152+ using BaseV2 = std::conditional_t <Const, const V2, V2>;
128153
129- constexpr iterator begin () const
130- requires std::ranges::range<const V1> && std::ranges::range<const V2>
131- {
132- return iterator (std::ranges::begin (_v1), std::ranges::end (_v1), std::ranges::begin (_v2));
133- }
154+ public:
155+ sentinel () = default ;
134156
135- constexpr sentinel end () {
136- return sentinel (std::ranges::end (_v2));
137- }
157+ constexpr explicit sentinel (std::ranges::sentinel_t <BaseV2> end2)
158+ : _end2(std::move(end2)) {}
138159
139- constexpr sentinel end () const
140- requires std::ranges::range<const V1> && std::ranges::range<const V2>
141- {
142- return sentinel (std::ranges::end (_v2));
143- }
160+ constexpr auto end2 () const {
161+ return _end2;
162+ }
144163
145- private:
146- V1 _v1;
147- V2 _v2;
164+ private:
165+ std::ranges::sentinel_t <BaseV2> _end2{};
166+
167+ friend class concat_view ;
168+ friend class iterator <Const>;
169+ };
148170};
149171
172+ namespace detail {
173+
150174struct concat_fn {
151175 template <std::ranges::viewable_range R1, std::ranges::viewable_range R2>
152176 constexpr auto operator ()(R1&& r1, R2&& r2) const {
0 commit comments