Tested on x86_64-pc-linux-gnu, does this look OK for trunk? -- >8 --
This patch proactively implements the proposed resolution for this LWG issue, which seems straightforward and slated to be approved as-is. I opted to not add a _GLIBCXX_RESOLVE_LIB_DEFECTS code comment for this since concat_view is C++26 and hasn't been shipped in a GCC release yet. libstdc++-v3/ChangeLog: * include/std/ranges (concat_view::begin): Add space after 'requires' starting a requires-clause. (concat_view::end): Refine condition for returning an iterator rather than default_sentinel as per LWG 4166. * testsuite/std/ranges/concat/1.cc (test03): Verify LWG 4166 example. --- libstdc++-v3/include/std/ranges | 10 ++++---- libstdc++-v3/testsuite/std/ranges/concat/1.cc | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 5b455888536..a6c4d66520c 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -9687,7 +9687,7 @@ namespace ranges { } constexpr iterator<false> - begin() requires(!(__detail::__simple_view<_Vs> && ...)) + begin() requires (!(__detail::__simple_view<_Vs> && ...)) { iterator<false> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views))); __it.template _M_satisfy<0>(); @@ -9703,9 +9703,10 @@ namespace ranges } constexpr auto - end() requires(!(__detail::__simple_view<_Vs> && ...)) + end() requires (!(__detail::__simple_view<_Vs> && ...)) { - if constexpr (__detail::__last_is_common<_Vs...>::value) + if constexpr ((semiregular<iterator_t<_Vs>> && ...) + && __detail::__last_is_common<_Vs...>::value) { constexpr auto __n = sizeof...(_Vs); return iterator<false>(this, in_place_index<__n - 1>, @@ -9718,7 +9719,8 @@ namespace ranges constexpr auto end() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...> { - if constexpr (__detail::__last_is_common<const _Vs...>::value) + if constexpr ((semiregular<iterator_t<const _Vs>> && ...) + && __detail::__last_is_common<const _Vs...>::value) { constexpr auto __n = sizeof...(_Vs); return iterator<true>(this, in_place_index<__n - 1>, diff --git a/libstdc++-v3/testsuite/std/ranges/concat/1.cc b/libstdc++-v3/testsuite/std/ranges/concat/1.cc index 6ffe28ece51..511a7e8a858 100644 --- a/libstdc++-v3/testsuite/std/ranges/concat/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/concat/1.cc @@ -10,6 +10,7 @@ #include <algorithm> #include <vector> #include <array> +#include <sstream> #include <utility> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -66,9 +67,31 @@ test02() VERIFY( ranges::equal(v | views::drop(1), x) ); } +void +test03() +{ + // LWG 4166 - concat_view::end() should be more constrained in order to + // support noncopyable iterators + auto range_copyable_it = std::vector<int>{1, 2, 3}; + + std::stringstream ss{"4 5 6"}; + auto range_noncopyable_it = views::istream<int>(ss); + + auto view1 = views::concat(range_copyable_it, range_noncopyable_it); + static_assert( ranges::range<decltype(view1)> ); + VERIFY( ranges::equal(view1, std::vector{1, 2, 3, 4, 5, 6}) ); + + ss = std::stringstream{"4 5 6"}; + range_noncopyable_it = views::istream<int>(ss); + auto view2 = views::concat(range_noncopyable_it, range_copyable_it); + static_assert( ranges::range<decltype(view2)> ); + VERIFY( ranges::equal(view2, std::vector{4, 5, 6, 1, 2, 3}) ); +} + int main() { static_assert(test01()); test02(); + test03(); } -- 2.47.0.107.g34b6ce9b30