diff --git a/stl/inc/spanstream b/stl/inc/spanstream index cf2347865b4..34e0622ea2a 100644 --- a/stl/inc/spanstream +++ b/stl/inc/spanstream @@ -207,6 +207,9 @@ public: explicit basic_ispanstream(_STD span<_Elem> _Span, ios_base::openmode _Which = ios_base::in) : _Mybase(_STD addressof(_Buf)), _Buf(_Span, _Which | ios_base::in) {} + explicit basic_ispanstream(_STD span _Span) + : basic_ispanstream(_STD span<_Elem>{const_cast<_Elem*>(_Span.data()), _Span.size()}) {} + basic_ispanstream(const basic_ispanstream&) = delete; basic_ispanstream(basic_ispanstream&& _Right) : _Mybase(_STD move(_Right)), _Buf(_STD move(_Right._Buf)) { @@ -217,8 +220,7 @@ public: requires ( !convertible_to<_ReadOnlyRange, _STD span<_Elem>> && convertible_to<_ReadOnlyRange, _STD span>) explicit basic_ispanstream(_ReadOnlyRange&& _Range) - : basic_ispanstream( - _STD span<_Elem>{const_cast<_Elem*>(_RANGES data(_Range)), static_cast(_RANGES size(_Range))}) {} + : basic_ispanstream(static_cast<_STD span>(_STD forward<_ReadOnlyRange>(_Range))) {} basic_ispanstream& operator=(const basic_ispanstream&) = delete; @@ -251,8 +253,8 @@ public: requires ( !convertible_to<_ReadOnlyRange, _STD span<_Elem>> && convertible_to<_ReadOnlyRange, _STD span>) void span(_ReadOnlyRange&& _Range) noexcept { - this->span( - _STD span<_Elem>{const_cast<_Elem*>(_RANGES data(_Range)), static_cast(_RANGES size(_Range))}); + const auto _Sp = static_cast<_STD span>(_STD forward<_ReadOnlyRange>(_Range)); + this->span(_STD span<_Elem>{const_cast<_Elem*>(_Sp.data()), _Sp.size()}); } private: diff --git a/tests/std/tests/P0448R4_spanstream/test.cpp b/tests/std/tests/P0448R4_spanstream/test.cpp index cf97ba7f317..77f69db28d8 100644 --- a/tests/std/tests/P0448R4_spanstream/test.cpp +++ b/tests/std/tests/P0448R4_spanstream/test.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -29,6 +31,28 @@ constexpr array input_std_array{'1', ' ', '2', ' ', '3', ' ', '4', ' ', '5'}; template <> inline constexpr array input_std_array{L'1', L' ', L'2', L' ', L'3', L' ', L'4', L' ', L'5'}; +template +list li{}; +// Neither sized_range nor contiguous_range. +template +struct SpecialRange { + auto begin() { + return std::begin(li); + } + auto end() { + return std::end(li); + } + operator span() const { + return span(input_view); + } +}; +template +constexpr bool ranges::enable_borrowed_range> = true; + +static_assert(!ranges::sized_range>); +static_assert(!ranges::contiguous_range>); +static_assert(ranges::borrowed_range>); + template class basic_test_buf : public Spanbuf { public: @@ -626,6 +650,30 @@ void test_ispanstream() { assert(static_cast(range_constructed.rdbuf())->epptr() == nullptr); } + { // GH-4879 : The span constructed by basic_ispanstream's range constructor may be ill-formed + const CharT buffer[17]{}; + span span_const_elem{buffer}; + basic_ispanstream span_const_elem_constructed{span_const_elem}; + assert(span_const_elem_constructed.span().data() == buffer); + assert(static_cast(span_const_elem_constructed.rdbuf())->eback() == buffer); + assert(static_cast(span_const_elem_constructed.rdbuf())->gptr() == buffer); + assert(static_cast(span_const_elem_constructed.rdbuf())->egptr() == end(buffer)); + assert(static_cast(span_const_elem_constructed.rdbuf())->pbase() == nullptr); + assert(static_cast(span_const_elem_constructed.rdbuf())->pptr() == nullptr); + assert(static_cast(span_const_elem_constructed.rdbuf())->epptr() == nullptr); + + SpecialRange special_range{}; + auto rr = static_cast>(special_range); + basic_ispanstream special_range_constructed{special_range}; + assert(special_range_constructed.span().data() == rr.data()); + assert(static_cast(special_range_constructed.rdbuf())->eback() == rr.data()); + assert(static_cast(special_range_constructed.rdbuf())->gptr() == rr.data()); + assert(static_cast(special_range_constructed.rdbuf())->egptr() == rr.data() + rr.size()); + assert(static_cast(special_range_constructed.rdbuf())->pbase() == nullptr); + assert(static_cast(special_range_constructed.rdbuf())->pptr() == nullptr); + assert(static_cast(special_range_constructed.rdbuf())->epptr() == nullptr); + } + { // span CharT buffer[10]; basic_ispanstream is{span{buffer}}; @@ -646,6 +694,14 @@ void test_ispanstream() { assert(is.span().data() == input_range.data()); assert(is.span().size() == input_range.size()); + // also test GH-4879 for basic_ispanstream::span(ROS&&) + is.span(span{other_buffer}); + assert(is.span().data() == other_buffer); + assert(is.span().size() == size(other_buffer)); + is.span(SpecialRange{}); + assert(is.span().data() == input_range.data()); + assert(is.span().size() == input_range.size()); + if constexpr (is_same_v) { const char const_buffer[] = "1 2 3 4 5"; basic_ispanstream is_const_buffer{span{const_buffer}};