From: Tomasz KamiƄski Date: Tue, 22 Jul 2025 07:44:24 +0000 (+0200) Subject: libstdc++: Make testsuite_iterators constexpr and expand inplace_vector tests [PR119137] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f59cb28d53b62aa080da60617109440b303ceb2b;p=thirdparty%2Fgcc.git libstdc++: Make testsuite_iterators constexpr and expand inplace_vector tests [PR119137] All functions in testsuite_iterators.h are now marked constexpr, targeting the earliest possible standard. Most functions use C++14 due to multi-statement bodies, with exceptions: * BoundsContainer and some constructors are C++11 compatible. * OutputContainer is C++20 due to operator new/delete usage. Before C++23, each constexpr templated function requires a constexpr -suitable instantiation. Functions delegating to _GLIBCXX14_CONSTEXPR must also be _GLIBCXX14_CONSTEXPR; e.g., forward_iterator_wrapper's constructor calling input_iterator_wrapper's constructor, or operator-> calling operator*. For classes defined C++20 or later (e.g., test_range), constexpr is applied unconditionally. PR libstdc++/119137 libstdc++-v3/ChangeLog: * testsuite/23_containers/inplace_vector/cons/from_range.cc: Run iterators and range test at compile-time. * testsuite/23_containers/inplace_vector/modifiers/assign.cc: Likewise. * testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc: Likewise. * testsuite/util/testsuite_iterators.h (__gnu_test::BoundsContainer) (__gnu_test::OutputContainer, __gnu_test::WritableObject) (__gnu_test::output_iterator_wrapper, __gnu_test::input_iterator_wrapper) (__gnu_test::forward_iterator_wrapper) (__gnu_test::bidirectional_iterator_wrapper) (__gnu_test::random_access_iterator_wrapper) (__gnu_test::test_container): Add appropriate _GLIBCXXNN_CONSTEXPR macros to member functions. (__gnu_test::contiguous_iterator_wrapper) (__gnu_test::input_iterator_wrapper_rval) (__gnu_test::test_range, __gnu_test::test_range_nocopy) (__gnu_test::test_sized_range_sized_sent) (__gnu_test::test_sized_range): Add constexpr specifier to member functions. --- diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range.cc index 5fa8a5de312..4a2f193e4a5 100644 --- a/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range.cc @@ -70,7 +70,7 @@ do_test_it() #endif } -bool +constexpr bool test_iterators() { using namespace __gnu_test; @@ -131,7 +131,7 @@ do_test_r() #endif } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -152,9 +152,9 @@ test_ranges() // Not lvalue-convertible to int struct C { - C(int v) : val(v) { } - operator int() && { return val; } - bool operator==(int b) const { return b == val; } + constexpr C(int v) : val(v) { } + constexpr operator int() && { return val; } + constexpr bool operator==(int b) const { return b == val; } int val; }; using rvalue_input_range = test_range; @@ -163,22 +163,15 @@ test_ranges() return true; } -constexpr bool -test_constexpr() -{ - // XXX: this doesn't test the non-forward_range code paths are constexpr. - std::initializer_list il{1, 2, 3, 4}; - std::inplace_vector v(il.begin(), il.end()); - eq(v, il); - - do_test_r>(); - return true; -} - int main() { - test_iterators(); - test_ranges(); - static_assert( test_constexpr() ); + auto test_all = [] { + test_iterators(); + test_ranges(); + return true; + }; + + test_all(); + static_assert( test_all() ); } diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/assign.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/assign.cc index 91132be550e..65b505e7f37 100644 --- a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/assign.cc +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/assign.cc @@ -204,14 +204,10 @@ template constexpr void test_assign_empty() { - // TODO make test iterators consteval - if !consteval - { - using namespace __gnu_test; - test_assign_empty_it(); - test_assign_empty_it(); - test_assign_empty_it(); - } + using namespace __gnu_test; + test_assign_empty_it(); + test_assign_empty_it(); + test_assign_empty_it(); test_assign_empty_other; } @@ -339,23 +335,20 @@ constexpr void test_assigns() { using namespace __gnu_test; - // TODO make test iterators consteval - if !consteval { - test_assign_range>(); - test_assign_range>(); - - test_assign_range>(); - test_assign_range>(); - test_assign_range>(); - - test_assign_range>(); - test_assign_range>(); - test_assign_range>(); - - test_assign_iterators(); - test_assign_iterators(); - test_assign_iterators(); - } + test_assign_range>(); + test_assign_range>(); + + test_assign_range>(); + test_assign_range>(); + test_assign_range>(); + + test_assign_range>(); + test_assign_range>(); + test_assign_range>(); + + test_assign_iterators(); + test_assign_iterators(); + test_assign_iterators(); test_assign_initializer_list(); test_assign_repeated(); diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc index 072f0b3e095..6a5b62f0aff 100644 --- a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc @@ -227,12 +227,10 @@ constexpr void test_add_to_full() { using namespace __gnu_test; - // TODO make test iterators consteval - if !consteval { - test_add_to_full_it(); - test_add_to_full_it(); - test_add_to_full_it(); - } + test_add_to_full_it(); + test_add_to_full_it(); + test_add_to_full_it(); + test_add_to_full_other(); } @@ -566,34 +564,31 @@ constexpr void test_inserts() { using namespace __gnu_test; - // TODO make test iterators consteval - if !consteval { - do_test_ranges>(); - do_test_ranges>(); + do_test_ranges>(); + do_test_ranges>(); - do_test_ranges>(); - do_test_ranges>(); - do_test_ranges>(); + do_test_ranges>(); + do_test_ranges>(); + do_test_ranges>(); - do_test_ranges>(); - do_test_ranges>(); - do_test_ranges>(); + do_test_ranges>(); + do_test_ranges>(); + do_test_ranges>(); - test_insert_iterators(); - test_insert_iterators(); - test_insert_iterators(); - } + test_insert_iterators(); + test_insert_iterators(); + test_insert_iterators(); - test_insert_initializer_list(); - test_insert_repeated(); +test_insert_initializer_list(); +test_insert_repeated(); } int main() { - auto test_all = []{ - test_add_to_full<0, int>(); - test_add_to_full<0, X>(); - test_add_to_full<4, int>(); +auto test_all = []{ + test_add_to_full<0, int>(); + test_add_to_full<0, X>(); + test_add_to_full<4, int>(); test_inserts(); #ifdef __cpp_lib_constexpr_inplace_vector diff --git a/libstdc++-v3/testsuite/util/testsuite_iterators.h b/libstdc++-v3/testsuite/util/testsuite_iterators.h index e9f8f56eb27..acd412af0f2 100644 --- a/libstdc++-v3/testsuite/util/testsuite_iterators.h +++ b/libstdc++-v3/testsuite/util/testsuite_iterators.h @@ -61,10 +61,12 @@ namespace __gnu_test T* first; T* last; + _GLIBCXX_CONSTEXPR BoundsContainer(T* _first, T* _last) : first(_first), last(_last) { } - std::size_t size() const { return last - first; } + _GLIBCXX_CONSTEXPR std::size_t + size() const { return last - first; } }; // Simple container for holding state of a set of output iterators. @@ -74,11 +76,13 @@ namespace __gnu_test T* incrementedto; bool* writtento; + _GLIBCXX20_CONSTEXPR OutputContainer(T* _first, T* _last) : BoundsContainer(_first, _last), incrementedto(_first), writtento(new bool[this->size()]()) { } + _GLIBCXX20_CONSTEXPR ~OutputContainer() { delete[] writtento; } }; @@ -92,12 +96,14 @@ namespace __gnu_test public: OutputContainer* SharedInfo; + _GLIBCXX_CONSTEXPR WritableObject(T* ptr_in, OutputContainer* SharedInfo_in): ptr(ptr_in), SharedInfo(SharedInfo_in) { } #if __cplusplus >= 201103L template + _GLIBCXX14_CONSTEXPR typename std::enable_if::value>::type operator=(U&& new_val) const { @@ -107,6 +113,7 @@ namespace __gnu_test } #else template + _GLIBCXX14_CONSTEXPR void operator=(const U& new_val) { @@ -128,6 +135,7 @@ namespace __gnu_test struct output_iterator_wrapper { protected: + _GLIBCXX_CONSTEXPR output_iterator_wrapper() : ptr(0), SharedInfo(0) { } @@ -142,6 +150,7 @@ namespace __gnu_test T* ptr; ContainerType* SharedInfo; + _GLIBCXX14_CONSTEXPR output_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : ptr(_ptr), SharedInfo(SharedInfo_in) { @@ -155,6 +164,7 @@ namespace __gnu_test operator=(const output_iterator_wrapper&) = default; #endif + _GLIBCXX14_CONSTEXPR WritableObject operator*() const { @@ -163,6 +173,7 @@ namespace __gnu_test return WritableObject(ptr, SharedInfo); } + _GLIBCXX14_CONSTEXPR output_iterator_wrapper& operator++() { @@ -173,6 +184,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR output_iterator_wrapper operator++(int) { @@ -224,13 +236,19 @@ namespace __gnu_test struct deref_proxy { T* ptr; - operator const T&() const { return *ptr; } + + _GLIBCXX_CONSTEXPR + operator const T&() const + { return *ptr; } } p; - deref_proxy operator*() const { return p; } + _GLIBCXX_CONSTEXPR + deref_proxy operator*() const + { return p; } }; protected: + _GLIBCXX_CONSTEXPR input_iterator_wrapper() : ptr(0), SharedInfo(0) { } @@ -245,6 +263,7 @@ namespace __gnu_test T* ptr; ContainerType* SharedInfo; + _GLIBCXX14_CONSTEXPR input_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : ptr(_ptr), SharedInfo(SharedInfo_in) { ITERATOR_VERIFY(ptr >= SharedInfo->first && ptr <= SharedInfo->last); } @@ -256,6 +275,7 @@ namespace __gnu_test operator=(const input_iterator_wrapper&) = default; #endif + _GLIBCXX14_CONSTEXPR bool operator==(const input_iterator_wrapper& in) const { @@ -264,32 +284,34 @@ namespace __gnu_test return ptr == in.ptr; } + _GLIBCXX14_CONSTEXPR bool operator!=(const input_iterator_wrapper& in) const { return !(*this == in); } - T* - base() const + _GLIBCXX_CONSTEXPR + T* base() const { return ptr; } - T& - operator*() const + _GLIBCXX14_CONSTEXPR + T& operator*() const { ITERATOR_VERIFY(SharedInfo && ptr < SharedInfo->last); ITERATOR_VERIFY(ptr >= SharedInfo->first); return *ptr; } - T* - operator->() const + _GLIBCXX14_CONSTEXPR + T* operator->() const { return &**this; } + _GLIBCXX14_CONSTEXPR input_iterator_wrapper& operator++() { @@ -300,6 +322,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR post_inc_proxy operator++(int) { @@ -340,10 +363,12 @@ namespace __gnu_test typedef BoundsContainer ContainerType; typedef std::forward_iterator_tag iterator_category; + _GLIBCXX14_CONSTEXPR forward_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : input_iterator_wrapper(_ptr, SharedInfo_in) { } + _GLIBCXX14_CONSTEXPR forward_iterator_wrapper() { } @@ -354,17 +379,18 @@ namespace __gnu_test operator=(const forward_iterator_wrapper&) = default; #endif - T& - operator*() const + _GLIBCXX14_CONSTEXPR + T& operator*() const { ITERATOR_VERIFY(this->SharedInfo && this->ptr < this->SharedInfo->last); return *(this->ptr); } - T* - operator->() const + _GLIBCXX14_CONSTEXPR + T* operator->() const { return &**this; } + _GLIBCXX14_CONSTEXPR forward_iterator_wrapper& operator++() { @@ -373,6 +399,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR forward_iterator_wrapper operator++(int) { @@ -382,8 +409,8 @@ namespace __gnu_test } #if __cplusplus >= 201402L - bool - operator==(const forward_iterator_wrapper& it) const noexcept + constexpr + bool operator==(const forward_iterator_wrapper& it) const noexcept { // Since C++14 value-initialized forward iterators are comparable. if (this->SharedInfo == nullptr || it.SharedInfo == nullptr) @@ -394,8 +421,8 @@ namespace __gnu_test return base_this == base_that; } - bool - operator!=(const forward_iterator_wrapper& it) const noexcept + constexpr + bool operator!=(const forward_iterator_wrapper& it) const noexcept { return !(*this == it); } @@ -415,10 +442,12 @@ namespace __gnu_test typedef BoundsContainer ContainerType; typedef std::bidirectional_iterator_tag iterator_category; + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : forward_iterator_wrapper(_ptr, SharedInfo_in) { } + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper() : forward_iterator_wrapper() { } @@ -431,6 +460,7 @@ namespace __gnu_test operator=(const bidirectional_iterator_wrapper&) = default; #endif + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper& operator++() { @@ -439,6 +469,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper operator++(int) { @@ -447,6 +478,7 @@ namespace __gnu_test return tmp; } + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper& operator--() { @@ -455,6 +487,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper operator--(int) { @@ -478,10 +511,12 @@ namespace __gnu_test typedef BoundsContainer ContainerType; typedef std::random_access_iterator_tag iterator_category; + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : bidirectional_iterator_wrapper(_ptr, SharedInfo_in) { } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper() : bidirectional_iterator_wrapper() { } @@ -494,6 +529,7 @@ namespace __gnu_test operator=(const random_access_iterator_wrapper&) = default; #endif + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper& operator++() { @@ -502,6 +538,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper operator++(int) { @@ -510,6 +547,7 @@ namespace __gnu_test return tmp; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper& operator--() { @@ -518,6 +556,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper operator--(int) { @@ -526,6 +565,7 @@ namespace __gnu_test return tmp; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper& operator+=(std::ptrdiff_t n) { @@ -542,10 +582,12 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper& operator-=(std::ptrdiff_t n) { return *this += -n; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper operator-(std::ptrdiff_t n) const { @@ -553,6 +595,7 @@ namespace __gnu_test return tmp -= n; } + _GLIBCXX14_CONSTEXPR std::ptrdiff_t operator-(const random_access_iterator_wrapper& in) const { @@ -560,42 +603,44 @@ namespace __gnu_test return this->ptr - in.ptr; } - T& - operator[](std::ptrdiff_t n) const + _GLIBCXX14_CONSTEXPR + T& operator[](std::ptrdiff_t n) const { return *(*this + n); } - bool - operator<(const random_access_iterator_wrapper& in) const + _GLIBCXX14_CONSTEXPR + bool operator<(const random_access_iterator_wrapper& in) const { ITERATOR_VERIFY(this->SharedInfo == in.SharedInfo); return this->ptr < in.ptr; } - bool - operator>(const random_access_iterator_wrapper& in) const + _GLIBCXX14_CONSTEXPR + bool operator>(const random_access_iterator_wrapper& in) const { return in < *this; } - bool - operator>=(const random_access_iterator_wrapper& in) const + _GLIBCXX14_CONSTEXPR + bool operator>=(const random_access_iterator_wrapper& in) const { return !(*this < in); } - bool - operator<=(const random_access_iterator_wrapper& in) const + _GLIBCXX14_CONSTEXPR + bool operator<=(const random_access_iterator_wrapper& in) const { return !(*this > in); } }; template + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper operator+(random_access_iterator_wrapper it, std::ptrdiff_t n) { return it += n; } template + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper operator+(std::ptrdiff_t n, random_access_iterator_wrapper it) { return it += n; } @@ -613,14 +658,17 @@ namespace __gnu_test { typename ItType::ContainerType bounds; + _GLIBCXX_CONSTEXPR test_container(T* _first, T* _last) : bounds(_first, _last) { } template explicit + _GLIBCXX_CONSTEXPR test_container(T (&arr)[N]) : bounds(arr, arr+N) { } + _GLIBCXX14_CONSTEXPR ItType it(int pos) { @@ -628,6 +676,7 @@ namespace __gnu_test return ItType(bounds.first + pos, &bounds); } + _GLIBCXX14_CONSTEXPR ItType it(T* pos) { @@ -635,18 +684,22 @@ namespace __gnu_test return ItType(pos, &bounds); } + _GLIBCXX_CONSTEXPR const T& val(int pos) { return (bounds.first)[pos]; } + _GLIBCXX14_CONSTEXPR ItType begin() { return it(bounds.first); } + _GLIBCXX14_CONSTEXPR ItType end() { return it(bounds.last); } + _GLIBCXX_CONSTEXPR std::size_t size() const { return bounds.size(); } @@ -686,6 +739,7 @@ namespace __gnu_test // Use an integer-class type to try and break the library code. using difference_type = std::ranges::__detail::__max_diff_type; + constexpr contiguous_iterator_wrapper& operator++() { @@ -693,6 +747,7 @@ namespace __gnu_test return *this; } + constexpr contiguous_iterator_wrapper& operator--() { @@ -700,6 +755,7 @@ namespace __gnu_test return *this; } + constexpr contiguous_iterator_wrapper operator++(int) { @@ -708,6 +764,7 @@ namespace __gnu_test return tmp; } + constexpr contiguous_iterator_wrapper operator--(int) { @@ -716,6 +773,7 @@ namespace __gnu_test return tmp; } + constexpr contiguous_iterator_wrapper& operator+=(difference_type n) { @@ -724,23 +782,28 @@ namespace __gnu_test return *this; } - friend contiguous_iterator_wrapper + friend constexpr + contiguous_iterator_wrapper operator+(contiguous_iterator_wrapper iter, difference_type n) { return iter += n; } - friend contiguous_iterator_wrapper + friend constexpr + contiguous_iterator_wrapper operator+(difference_type n, contiguous_iterator_wrapper iter) { return iter += n; } + constexpr contiguous_iterator_wrapper& operator-=(difference_type n) { return *this += -n; } - friend contiguous_iterator_wrapper + friend constexpr + contiguous_iterator_wrapper operator-(contiguous_iterator_wrapper iter, difference_type n) { return iter -= n; } - friend difference_type + friend constexpr + difference_type operator-(contiguous_iterator_wrapper l, contiguous_iterator_wrapper r) { const random_access_iterator_wrapper& lbase = l; @@ -748,6 +811,7 @@ namespace __gnu_test return static_cast(lbase - rbase); } + constexpr decltype(auto) operator[](difference_type n) const { auto d = static_cast(n); @@ -765,6 +829,7 @@ namespace __gnu_test { using input_iterator_wrapper::input_iterator_wrapper; + constexpr input_iterator_wrapper_nocopy() : input_iterator_wrapper(nullptr, nullptr) { } @@ -779,6 +844,7 @@ namespace __gnu_test using input_iterator_wrapper::operator++; + constexpr input_iterator_wrapper_nocopy& operator++() { @@ -795,6 +861,7 @@ namespace __gnu_test using input_iterator_wrapper::operator++; + constexpr input_iterator_wrapper_rval& operator++() { @@ -802,8 +869,8 @@ namespace __gnu_test return *this; } - T&& - operator*() const + constexpr + T&& operator*() const { return std::move(input_iterator_wrapper::operator*()); } }; @@ -821,7 +888,9 @@ namespace __gnu_test using Iter::operator++; - iterator& operator++() { Iter::operator++(); return *this; } + constexpr + iterator& operator++() + { Iter::operator++(); return *this; } }; template @@ -829,21 +898,24 @@ namespace __gnu_test { T* end; - friend bool operator==(const sentinel& s, const I& i) noexcept + friend constexpr bool + operator==(const sentinel& s, const I& i) noexcept { return s.end == i.ptr; } - friend auto operator-(const sentinel& s, const I& i) noexcept + friend constexpr + auto operator-(const sentinel& s, const I& i) noexcept requires std::random_access_iterator { return std::iter_difference_t(s.end - i.ptr); } - friend auto operator-(const I& i, const sentinel& s) noexcept + friend constexpr auto + operator-(const I& i, const sentinel& s) noexcept requires std::random_access_iterator { return std::iter_difference_t(i.ptr - s.end); } }; protected: - auto - get_iterator(T* p) + constexpr + auto get_iterator(T* p) { if constexpr (std::default_initializable>) return Iter(p, &bounds); @@ -852,17 +924,19 @@ namespace __gnu_test } public: + constexpr test_range(T* first, T* last) : bounds(first, last) { } template - explicit + explicit constexpr test_range(T (&arr)[N]) : test_range(arr, arr+N) { } - auto begin() & { return get_iterator(bounds.first); } + constexpr auto begin() & + { return get_iterator(bounds.first); } - auto end() & + constexpr auto end() & { using I = decltype(get_iterator(bounds.last)); return sentinel{bounds.last}; @@ -875,7 +949,9 @@ namespace __gnu_test template class Iter> struct test_range_nocopy : test_range { - test_range_nocopy(T* first, T* last) : test_range(first, last) + constexpr + test_range_nocopy(T* first, T* last) + : test_range(first, last) {} test_range_nocopy(test_range_nocopy&&) = default; @@ -910,6 +986,7 @@ namespace __gnu_test { using test_range::test_range; + constexpr std::size_t size() const noexcept { return this->bounds.size(); } }; @@ -945,18 +1022,22 @@ namespace __gnu_test { T* end; - friend bool operator==(const sentinel& s, const I& i) noexcept + friend constexpr + bool operator==(const sentinel& s, const I& i) noexcept { return s.end == i.ptr; } - friend std::iter_difference_t + friend constexpr + std::iter_difference_t operator-(const sentinel& s, const I& i) noexcept { return std::iter_difference_t(s.end - i.ptr); } - friend std::iter_difference_t + friend constexpr + std::iter_difference_t operator-(const I& i, const sentinel& s) noexcept { return std::iter_difference_t(i.ptr - s.end); } }; + constexpr auto end() & { using I = decltype(this->get_iterator(this->bounds.last));