2323#include " test_macros.h"
2424#include " ../types.h"
2525
26- template <bool NoThrow>
26+ template <typename T, bool NoThrow>
2727struct ThowingIter {
2828 using iterator_concept = std::forward_iterator_tag;
2929 using iterator_category = std::forward_iterator_tag;
3030 using difference_type = std::ptrdiff_t ;
31- using value_type = int ;
31+ using value_type = T ;
3232
33- int * p = nullptr ;
33+ T * p = nullptr ;
3434
35- constexpr int & operator *() const noexcept { return *p; }
35+ constexpr T & operator *() const noexcept { return *p; }
3636 constexpr ThowingIter& operator ++() noexcept {
3737 ++p;
3838 return *this ;
@@ -44,7 +44,7 @@ struct ThowingIter {
4444 }
4545 friend constexpr bool operator ==(ThowingIter, ThowingIter) = default ;
4646
47- friend constexpr int && iter_move(const ThowingIter& it) noexcept (NoThrow) { return std::move (*it.p ); }
47+ friend constexpr T && iter_move(const ThowingIter& it) noexcept (NoThrow) { return std::move (*it.p ); }
4848};
4949
5050struct Range : std::ranges::view_base {
@@ -59,61 +59,26 @@ struct Range : std::ranges::view_base {
5959 int * end_;
6060};
6161
62- struct ThrowingValue {
63- int v{};
64- ThrowingValue () = default ;
65- explicit ThrowingValue (int x) noexcept (false ) : v(x) {}
66- ThrowingValue (const ThrowingValue&) noexcept (false ) = default ;
67- ThrowingValue (ThrowingValue&&) noexcept (false ) = default ;
68- };
69-
70- template <bool DerefNoThrow>
71- struct PValIter {
72- using iterator_concept = std::input_iterator_tag;
73- using iterator_category = std::input_iterator_tag;
74- using difference_type = std::ptrdiff_t ;
75- using value_type = std::conditional_t <DerefNoThrow, int , ThrowingValue>;
76-
77- int * p = nullptr ;
78-
79- decltype (auto ) operator *() const noexcept (DerefNoThrow) {
80- if constexpr (DerefNoThrow)
81- return *p; // int (noexcept)
82- else
83- return ThrowingValue{*p}; // not noexcept
84- }
85- PValIter& operator ++() noexcept {
86- ++p;
87- return *this ;
88- }
89- void operator ++(int ) noexcept { ++p; }
90- friend bool operator ==(PValIter, PValIter) = default ;
91- };
92-
93- static_assert (std::input_iterator<LRefIter<true >>);
94- static_assert (std::input_iterator<LRefIter<false >>);
95- static_assert (std::input_iterator<PValIter<true >>);
96- static_assert (std::input_iterator<PValIter<false >>);
97-
98- template <class Iter >
62+ template <class Iter , class Sentinel >
9963struct MiniView : std::ranges::view_base {
100- Iter b{}, e{};
64+ Iter b{};
65+ Sentinel e{};
10166 constexpr MiniView () = default;
102- constexpr MiniView (Iter first, Iter last) : b(first), e(last) {}
67+ constexpr MiniView (Iter first, Sentinel last) : b(first), e(last) {}
10368 constexpr Iter begin () const noexcept { return b; }
104- constexpr Iter end () const noexcept { return e; }
69+ constexpr Sentinel end () const noexcept { return e; }
10570};
10671
10772constexpr bool test () {
10873 int buf1[] = {1 , 2 , 3 , 4 };
10974 int buf2[] = {5 , 6 , 7 };
11075 {
11176 // All underlying iter_move are noexcept -> concat iterator's iter_move is noexcept
112- using I1 = ThowingIter<true >;
113- using S1 = sentinel_wrapper<I1 >;
114- using V1 = MiniView<I1 >;
115- V1 v1 (I1 (buf1), I1 ( buf1 + 4 ));
116- V1 v2 (I1 (buf2), I1 ( buf2 + 3 ));
77+ using Iter_NoThrow = ThowingIter<int , true >;
78+ using Sentinel_NoThrow = sentinel_wrapper<Iter_NoThrow >;
79+ using View_NoThrow = MiniView<Iter_NoThrow, Sentinel_NoThrow >;
80+ View_NoThrow v1 (Iter_NoThrow (buf1), Sentinel_NoThrow ( Iter_NoThrow ( buf1 + 4 ) ));
81+ View_NoThrow v2 (Iter_NoThrow (buf2), Sentinel_NoThrow ( Iter_NoThrow ( buf2 + 3 ) ));
11782
11883 auto cv = std::views::concat (v1, v2);
11984 using Iter = decltype (cv.begin ());
@@ -126,6 +91,25 @@ constexpr bool test() {
12691 (void )std::ranges::iter_move (it);
12792 }
12893
94+ {
95+ // One underlying may throw -> concat iter_move is NOT noexcept
96+ using Iter_NoThrow = ThowingIter<int , true >;
97+ using Iter_Throw = ThowingIter<int , false >;
98+ using Sentinel_NoThrow = sentinel_wrapper<Iter_NoThrow>;
99+ using Sentinel_Throw = sentinel_wrapper<Iter_Throw>;
100+ using View_NoThrow = MiniView<Iter_NoThrow, Sentinel_NoThrow>;
101+ using View_Throw = MiniView<Iter_Throw, Sentinel_Throw>;
102+
103+ auto cv = std::views::concat (View_NoThrow{Iter_NoThrow{buf1}, Sentinel_NoThrow{Iter_NoThrow{buf1 + 4 }}},
104+ View_Throw{Iter_Throw{buf2}, Sentinel_Throw{Iter_Throw{buf2 + 3 }}});
105+
106+ using Iter = decltype (cv.begin ());
107+ using CIter = decltype (std::as_const (cv).begin ());
108+
109+ static_assert (!noexcept (std::ranges::iter_move (std::declval<Iter&>())));
110+ static_assert (!noexcept (std::ranges::iter_move (std::declval<CIter&>())));
111+ }
112+
129113 return true ;
130114}
131115
0 commit comments